【问题标题】:Combining multiple regex substitutions组合多个正则表达式替换
【发布时间】:2012-07-11 22:28:48
【问题描述】:

我正在尝试使用正则表达式从文本块中删除一些内容。我已准备好所有模式,但我似乎无法删除两个(或更多)重叠的部分。

例如:

import re

r1 = r'I am'
r2 = r'am foo'

text = 'I am foo'

re.sub(r1, '', text)   # Returns ' foo'
re.sub(r2, '', text)   # Returns 'I '

如何同时替换两个匹配项并以空字符串结尾?


我最终使用了Ned Batchelder's answer 的略微修改版本:

def clean(self, text):
  mask = bytearray(len(text))

  for pattern in patterns:
    for match in re.finditer(pattern, text):
      r = range(match.start(), match.end())

      mask[r] = 'x' * len(r)

  return ''.join(character for character, bit in zip(text, mask) if not bit)

【问题讨论】:

    标签: python regex


    【解决方案1】:

    如您所示,您无法通过连续的re.sub 调用来做到这一点。您可以使用re.finditer 找到它们。每个匹配都会为您提供一个匹配对象,该对象具有.start.end 属性来指示它们的位置。您可以将所有这些收集在一起,然后在最后删除字符。

    这里我使用bytearray 作为可变字符串,用作掩码。它被初始化为零字节,并且我用“x”标记与任何正则表达式匹配的所有字节。然后我使用位掩码选择要保留在原始字符串中的字符,并构建一个仅包含不匹配字符的新字符串:

    bits = bytearray(len(text))
    for pat in patterns:
        for m in re.finditer(pat, text):
            bits[m.start():m.end()] = 'x' * (m.end()-m.start())
    new_string = ''.join(c for c,bit in zip(text, bits) if not bit)
    

    【讨论】:

    • 我从没想过匹配对象的startend 属性。我非常确定这会起作用,所以谢谢!
    • 很好的答案!我在startend 中添加了(),因为这些是方法,而不是属性。
    【解决方案2】:

    不要沮丧,但简短的回答是我很确定你不能。你能改变你的正则表达式,使它不需要重叠吗?

    如果您仍想这样做,我会尝试跟踪在原始字符串上进行的每个匹配的开始和停止索引。然后遍历字符串,只保留不在任何删除范围内的字符?

    【讨论】:

      【解决方案3】:

      一种非常有效的解决方案来自... Perl 将正则表达式合二为一:

      # aptitude install regexp-assemble
      $ regexp-assemble 
      I am
      I am foo
      Ctrl + D
      I am(?: foo)?
      

      regexp-assemble 获取您想要匹配的正则表达式或字符串的所有变体,然后 将它们合二为一。是的,它将最初的问题改变为另一个问题,因为它不再是匹配重叠的正则表达式,而是结合正则表达式进行匹配

      然后你就可以在你的代码中使用它了:

      $ python
      Python 2.7.3 (default, Aug  1 2012, 05:14:39) 
      [GCC 4.6.3] on linux2
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import re
      >>> re.sub("I am foo","I am(?: foo)?","")
      ''
      

      在 python 中移植 Regexp::Assemble 会很好:)

      【讨论】:

      • 命令是aptitude install libregexp-assemble-perl。我无法快速找到您指定名称的先前软件包的任何痕迹,但也许您使用的是不同的发行版;这是 Debian 稳定版。
      • 另外,在旧版本的软件包中,演示仅在 /usr/share/doc/libregexp-assemble-perl/examples/assemble.gz 中——我希望在 squeeze 框中使用此内容,其中未使用您指定的名称安装脚本。
      【解决方案4】:

      这是一种替代方法,它使用带有选择器迭代器的文本上的itertools.compress 动态过滤字符串。如果应该保留字符,则选择器返回Trueselector_for_patterns 为每个模式创建一个选择器。选择器与 all 函数相结合(仅当所有模式都想保留一个字符时,它应该在结果字符串中)。

      import itertools
      import re
      
      def selector_for_pattern(text, pattern):
          i = 0
          for m in re.finditer(pattern, text):
              for _ in xrange(i, m.start()):
                  yield True
              for _ in xrange(m.start(), m.end()):
                  yield False
              i = m.end()
          for _ in xrange(i, len(text)):
              yield True
      
      def clean(text, patterns):
          gen = [selector_for_pattern(text, pattern) for pattern in patterns]
          selector = itertools.imap(all, itertools.izip(* gen))
          return "".join(itertools.compress(text, selector))
      

      【讨论】:

        猜你喜欢
        • 2015-02-21
        • 1970-01-01
        • 2012-02-16
        • 2020-01-29
        • 2014-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多