【问题标题】:Why does re.findall return a list of tuples when my pattern only contains one group?当我的模式只包含一个组时,为什么 re.findall 会返回一个元组列表?
【发布时间】:2014-08-26 22:41:50
【问题描述】:

假设我有一个字符串s,其中包含字母和两个分隔符12。我想按以下方式拆分字符串:

  • 如果子字符串t介于12之间,返回t
  • 否则,返回每个字符

所以如果s = 'ab1cd2efg1hij2k',预期的输出是['a', 'b', 'cd', 'e', 'f', 'g', 'hij', 'k']

我尝试使用正则表达式:

import re
s = 'ab1cd2efg1hij2k'
re.findall( r'(1([a-z]+)2|[a-z])', s )

[('a', ''),
 ('b', ''),
 ('1cd2', 'cd'),
 ('e', ''),
 ('f', ''),
 ('g', ''),
 ('1hij2', 'hij'),
 ('k', '')]

从那里我可以通过[ x[x[-1]!=''] for x in re.findall( r'(1([a-z]+)2|[a-z])', s ) ] 得到我的答案,但我仍然不明白输出。 documentation 表示如果模式有多个组,findall 返回一个元组列表。但是,我的模式只包含一组。欢迎任何解释。

【问题讨论】:

    标签: python regex findall


    【解决方案1】:

    只需要做一个简单的更改:将组更改为非捕获组

    代码:

    import re
    s = 'ab1cd2efg1hij2k'
    re.findall( r'(1(?:[a-z]+)2|[a-z])', s )
    

    输出:

    ['a', 'b', '1cd2', 'e', 'f', 'g', '1hij2', 'k']
    

    【讨论】:

      【解决方案2】:

      如果您想在不拆分为匹配组的情况下进行“或”匹配,只需在“或”匹配的开头添加一个“?:”即可。

      没有'?:'

      re.findall('(test (word1|word2))', 'test word1')
      
      Output:
      [('test word1', 'word1')]
      

      带'?:'

      re.findall('(test (?:word1|word2))', 'test word1')
      
      Output:
      ['test word1']
      

      进一步解释:https://www.ocpsoft.org/tutorials/regular-expressions/or-in-regex/

      【讨论】:

      • "non-capturing group" 是这里的关键字...(仅为搜索引擎添加)
      【解决方案3】:

      我迟到了 5 年,但我想我可能已经找到了一个优雅的解决方案来解决 re.findall() 带有多个捕获组的丑陋的元组输出。

      一般来说,如果你最终得到一个看起来像这样的输出:

      [('pattern_1', '', ''), ('', 'pattern_2', ''), ('pattern_1', '', ''), ('', '', 'pattern_3')]
      

      然后你可以用这个小技巧把它变成一个平面列表:

      ["".join(x) for x in re.findall(all_patterns, iterable)]
      

      预期的输出将是这样的:

      ['pattern_1', 'pattern_2', 'pattern_1', 'pattern_3']
      

      它在 Python 3.7 上进行了测试。希望对您有所帮助!

      【讨论】:

        【解决方案4】:

        查看类似问题的答案:https://bugs.python.org/issue6663 如果您使用的是 findall,请去掉括号:

        import re
        s = 'ab1cd2efg1hij2k'
        re.findall( r'(?<=1)[a-z]+(?=2)|[a-z]', s )
        

        【讨论】:

          【解决方案5】:

          您的正则表达式有 2 个组,只需查看您使用的括号数 :)。一组是([a-z]+),另一组是(1([a-z]+)2|[a-z])。关键是您可以在其他组中拥有组。因此,如果可能,您应该构建一个只有一组的正则表达式,这样您就不必对结果进行后处理。

          只有一组的正则表达式示例如下:

          >>> import re
          >>> s = 'ab1cd2efg1hij2k'
          >>> re.findall('((?<=1)[a-z]+(?=2)|[a-z])', s)
          ['a', 'b', 'cd', 'e', 'f', 'g', 'hij', 'k']
          

          【讨论】:

            【解决方案6】:

            你的模式有两个组,更大的组:

            (1([a-z]+)2|[a-z])
            

            和第二个较小的组,它是您的第一个组的 子集

            ([a-z]+)
            

            这是一个可以为您提供预期结果的解决方案,但请注意,它真的很难看,可能有更好的方法。我就是想不通:

            import re
            s = 'ab1cd2efg1hij2k'
            a = re.findall( r'((?:1)([a-z]+)(?:2)|([a-z]))', s )
            a = [tuple(j for j in i if j)[-1] for i in a]
            
            >>> print a
            ['a', 'b', 'cd', 'e', 'f', 'g', 'hij', 'k']
            

            【讨论】:

            • 你的模式很奇怪。您不需要围绕12 的非捕获组,或围绕整个模式的组(您需要花费大量精力来跳过输出)。相反,只需接受 findall 调用将返回 2 元组并使用 a = [x or y for x, y in a] 将它们转换为单个值。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-06-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多