【问题标题】:Match all identifiers in a string匹配字符串中的所有标识符
【发布时间】:2019-11-15 15:41:07
【问题描述】:

问题:

我正在寻找一种方法来匹配给定行中的某些标识符 以某些词开头。 ID 包括 字符,可能后跟数字,然后是破折号,然后是一些 更多的数字。一个 ID 应该只匹配 起始词是以下之一:关闭、修复、解决。如果一个 行包含多个 ID,它们将由 字符串and。任意数量的 ID 可以出现在一个 行。

示例测试字符串:

Closes PD-1                                           # Match: PD-1

Related to PD-2                                       # No match, line doesn't start with an allowed word

Closes                                                
NPD-1                                                 # No match, as the identifier is in a new line

Fixes PD-21 and PD-22                                 # Match: PD-21, PD-22

Closes PD-31, also PD-32 and PD-33                    # Match: PD-31 - the rest is not captured because of ", also"
Resolves PD4-41 and PD4-42 and PD4-43 and PD4-44      # Match: PD4-41, PD4-42, PD4-43, PD4-44

Resolves something related to N-2                     # No match, the identifier is not directly after 'Resolves'

我尝试了什么:

使用正则表达式来获取所有匹配项,我总是在某些方面做得不够。例如。我试过的正则表达式之一是这样的:

^(?:Closes|Fixes|Resolves) (\w+-\d+)(?:(?: and )(\w+-\d+))*

  1. 我打算在线路需要的地方设置一个非捕获组 以允许的单词之一开头,后跟一个空格: ^(?:Closes|Fixes|Resolves)
  2. 那么至少需要一个ID跟在起始词后面, 我打算捕获:(\w+-\d+)
  3. 最后,零个或多个 ID 可以跟随第一个,它们是 由字符串and 分隔,但我只想捕获 这里是 ID,而不是分隔符:(?:(?: and )(\w+-\d+))*

这个正则表达式在 python 中的结果:

test_string = """
Closes PD-1                                           # Match: PD-1
Related to PD-2                                       # No match, line doesn't start with an allowed word
Closes                                                
NPD-1                                                 # No match, as the identifier is in a new line
Fixes PD-21 and PD-22                                 # Match: PD-21, PD-22
Closes PD-31, also PD-32 and PD-33                    # Match: PD-31 - the rest is not captured because of ", also"
Resolves PD4-41 and PD4-42 and PD4-43 and PD4-44      # Match: PD4-41, PD4-42, PD4-43, PD4-44
Resolves something related to N-2                     # No match, the identifier is not directly after 'Resolves'
"""

ids = []

for match in re.findall("^(?:Closes|Fixes|Resolves) (\w+-\d+)(?:(?: and )(\w+-\d+))*", test_string, re.M):
    for group in match:
        if group:
            ids.append(group)

print(ids)
['PD-1', 'PD-21', 'PD-22', 'PD-31', 'PD4-41', 'PD4-44']

Also, here is the result on regex101.com。如果在第一个 ID 之后有多个 ID,不幸的是它只会捕获最后一个匹配项,而不是所有匹配项。我读到重复捕获组只会捕获最后一次迭代,我应该在重复组周围放置一个捕获组以捕获所有迭代,但我无法让它工作。

总结

有没有正则表达式的解决方案,类似于我尝试的方法,但它捕获了所有出现的 ID?或者有没有更好的方法来解析这个字符串的 ID,使用 Python?

【问题讨论】:

    标签: python regex python-3.x


    【解决方案1】:

    您可以使用单个捕获组,并在该捕获组中匹配第一次出现并重复相同的模式 0 次以上,前面是空格,然后是 and 和空格。

    这些值在第 1 组中。

    要获取单独的值,请拆分 and

    ^(?:Closes|Fixes|Resolves) (\w+-\d+(?: and \w+-\d+)*)
    

    Regex demo

    【讨论】:

    【解决方案2】:

    两阶段方法可能更容易,例如:

    def get_matches(test):  #assume test is a list of strings
        regex1 = re.compile(r'^(?:Closes|Fixes|Resolves) \w+-\d+')
        regex2 = re.compile(r'\w+-\d+')
        results = []
        for line in test:
            if regex1.search(line):
                results.extend(regex2.findall(line))
        return results
    

    给予:

    ['PD-1','PD-21','PD-22','PD-31','PD-32', 
    'PD-33','PD4-41','PD4-42','PD4-43','PD4-44']
    

    【讨论】:

      【解决方案3】:

      如果您需要使用重复捕获组,您应该安装 PyPi regex 模块和 pip install regex 并使用

      import regex
      
      test_string = "your string here"
      ids = []
      for match in regex.finditer("^(?:Closes|Fixes|Resolves) (?P<id>\w+-\d+)(?:(?: and )(?P<id>\w+-\d+))*", test_string, regex.M):
          ids.extend(match.captures("id"))
      print(ids)
      # => ['PD-1', 'PD-21', 'PD-22', 'PD-31', 'PD4-41', 'PD4-42', 'PD4-43', 'PD4-44']
      

      Python demo

      每个组的捕获堆栈可通过match.captures(X) 访问。

      您拥有的正则表达式可以按原样使用,但这里有一个命名的捕获组对用户更友好。

      【讨论】:

        猜你喜欢
        • 2018-10-28
        • 1970-01-01
        • 2012-07-18
        • 1970-01-01
        • 2013-06-22
        • 1970-01-01
        • 2013-10-14
        • 1970-01-01
        • 2023-01-06
        相关资源
        最近更新 更多