正则表达式以重复模式捕获每个组的第一次出现
Regex capturing the first occurrence of every group in a recurring pattern
假设我有以下文本:
Name: John Doe\tAddress: Street 123 ABC\tCity: MyCity
我有一个正则表达式(有点复杂,但归结为这个):
^(?:(?:(?:Name: (.+?))|(?:Address: (.+?))|(?:City: (.+?)))\t*)+$
它有三个捕获组,可以捕获名称、地址和城市的值(如果它们出现在文本中)。这里还有几个例子:https://regex101.com/r/37nemH/6。 EDIT 预先排序不固定,也可能字段 不是 由 \t
个字符分隔。
现在一切正常,我遇到的唯一小问题是同一个文本中的一个字段出现两次,正如我放在 regex101 上的最后一个示例所示:
Name: John Doe\tAddress: Street 123 ABC\tCity: MyCity\tAddress: Other Address
我想要的是第二个捕获组匹配第一个地址,即Street 123 ABC
,最好让第二次出现在"City"组,即
1: John Doe
2: Street 123 ABC
3: MyCity\tAddress: Other Address
从概念上讲,我尝试用消极的后视来做到这一点,例如将 (?:Address: (.+?))
替换为 (?:(?<!.*Address: )Address: (.+?))
,即确保 Address:
匹配不会在文本中的某处由另一个 Address:
标记进行。但是,负后视不允许任意长度,所以这显然行不通。
这可以使用正则表达式实现吗?如何实现?
如果词序可以任意,部分或全部项目可以缺失,使用3个独立的模式来提取你需要的位会容易得多。
姓名 (demo):
^.*?Name:\s*(.*?)(?=\s*(?:Name:|Address:|City:|$))
城市 (demo):
^.*?City:\s*(.*?)(?=\s*(?:Name:|Address:|City:|$))
地址 (demo):
^.*?Address:\s*(.*?)(?=\s*(?:Name:|Address:|City:|$))
详情
^
- 字符串开头
.*?
- 除换行字符外的任何 0+ 个字符,尽可能少
Address:
- 停止并寻找预期匹配项的关键字
\s*
- 0+ 个空格
(.*?)
- 第 1 组:除换行符以外的任何 0+ 个字符,尽可能少...
(?=\s*(?:Name:|Address:|City:|$))
- 最多但不包括 0 个或更多空格,后跟 Name:
、Address:
、City:
或字符串结尾。
对于您提出的问题,您可以将此正则表达式与条件结构一起使用:
^.*?(?:(?:Name: (.+?)|(Address: )(.+?)|City: ((?(2).*?Address: )*.+?))\t*)+$
您的值在捕获的组 1、3、4 中可用。
捕获组 2 用于文字标签 "Address: "
。
这里,(?(2).*?Address: )*
是一个条件结构,这意味着如果捕获的组 2 存在,则在组 4 中匹配文本,直到找到下一个 Address:
(0 个或更多匹配项)。
对于文本 Name: John Doe Address: Street 123 ABC City: MyCity Address: Second address
,它将有以下匹配项:
Group 1. 169-177 `John Doe`
Group 2. 178-187 `Address: `
Group 3. 187-201 `Street 123 ABC`
Group 4. 210-240 `MyCity Address: Second address`
假设我有以下文本:
Name: John Doe\tAddress: Street 123 ABC\tCity: MyCity
我有一个正则表达式(有点复杂,但归结为这个):
^(?:(?:(?:Name: (.+?))|(?:Address: (.+?))|(?:City: (.+?)))\t*)+$
它有三个捕获组,可以捕获名称、地址和城市的值(如果它们出现在文本中)。这里还有几个例子:https://regex101.com/r/37nemH/6。 EDIT 预先排序不固定,也可能字段 不是 由 \t
个字符分隔。
现在一切正常,我遇到的唯一小问题是同一个文本中的一个字段出现两次,正如我放在 regex101 上的最后一个示例所示:
Name: John Doe\tAddress: Street 123 ABC\tCity: MyCity\tAddress: Other Address
我想要的是第二个捕获组匹配第一个地址,即Street 123 ABC
,最好让第二次出现在"City"组,即
1: John Doe
2: Street 123 ABC
3: MyCity\tAddress: Other Address
从概念上讲,我尝试用消极的后视来做到这一点,例如将 (?:Address: (.+?))
替换为 (?:(?<!.*Address: )Address: (.+?))
,即确保 Address:
匹配不会在文本中的某处由另一个 Address:
标记进行。但是,负后视不允许任意长度,所以这显然行不通。
这可以使用正则表达式实现吗?如何实现?
如果词序可以任意,部分或全部项目可以缺失,使用3个独立的模式来提取你需要的位会容易得多。
姓名 (demo):
^.*?Name:\s*(.*?)(?=\s*(?:Name:|Address:|City:|$))
城市 (demo):
^.*?City:\s*(.*?)(?=\s*(?:Name:|Address:|City:|$))
地址 (demo):
^.*?Address:\s*(.*?)(?=\s*(?:Name:|Address:|City:|$))
详情
^
- 字符串开头.*?
- 除换行字符外的任何 0+ 个字符,尽可能少Address:
- 停止并寻找预期匹配项的关键字\s*
- 0+ 个空格(.*?)
- 第 1 组:除换行符以外的任何 0+ 个字符,尽可能少...(?=\s*(?:Name:|Address:|City:|$))
- 最多但不包括 0 个或更多空格,后跟Name:
、Address:
、City:
或字符串结尾。
对于您提出的问题,您可以将此正则表达式与条件结构一起使用:
^.*?(?:(?:Name: (.+?)|(Address: )(.+?)|City: ((?(2).*?Address: )*.+?))\t*)+$
您的值在捕获的组 1、3、4 中可用。
捕获组 2 用于文字标签 "Address: "
。
这里,(?(2).*?Address: )*
是一个条件结构,这意味着如果捕获的组 2 存在,则在组 4 中匹配文本,直到找到下一个 Address:
(0 个或更多匹配项)。
对于文本 Name: John Doe Address: Street 123 ABC City: MyCity Address: Second address
,它将有以下匹配项:
Group 1. 169-177 `John Doe`
Group 2. 178-187 `Address: `
Group 3. 187-201 `Street 123 ABC`
Group 4. 210-240 `MyCity Address: Second address`