具有重复性的非贪婪正则表达式

时间:2014-09-28 21:13:46

标签: python regex string


我使用以下正则表达式:
    ((FFD8FF).+?((FFD9)(?:(?!FFD8).)*))

我需要使用正则表达式执行以下操作:

  • 查找FFD8FF
  • 查找下一个FFD9
  • 之前的最后一个FFD8FF
  • 停在最后FFD9,不包括
  • 之后的任何内容

    我所需要的是除了它找到并保留最后一个FFD9之后的任何垃圾。如何让它跳回上一个FFD9

    这是我用这个表达式搜索的字符串:

      

    asdfasdfasasdaFFD8FFasdfalsjdflajsdfljasdfasdfasdfasdfFFD9asdflasdflasdfFFD9asdfasdfFFD8FFasdfalsjdflajsdfljasdfasdfasdfasdfFFD9

    非常感谢你的帮助。

    更多信息:

    我有一个需要搜索的起始值和结束值列表(FFD8FF和FFD9只是一对)。他们在列表中。因此,我使用r.compile在for循环中动态创建表达式,该循环遍历不同的值。我有以下代码,但它返回0匹配:

    regExp = re.compile("FD8FF(?:[^F]|F(?!FD8FF))*FFD9") matchObj = re.findall(regExp, contents)

    在上面的代码中,我只是尝试使用普通的正则表达式,甚至没有从列表中获取值(看起来像这样):

    regExp = re.compile(typeItem[0] + "(?:[^" + typeItem[0][0] + "]|" + typeItem[0][0] + "(?!" + typeItem[0] + "))*" + typeItem[1])

    为什么没有任何匹配的任何其他想法?

    编辑:

    我发现我忘了包含旗帜。现在包含标志以忽略大小写和多行。我现在有

    regExp = re.compile(typeItem[0] + "(?:[^" + typeItem[0][0] + "]|" + typeItem[0][0] + "(?!" + typeItem[0] + "))*" + typeItem[1],re.M|re.I)

    虽然现在我收到了内存错误。有没有办法让这个更有效率?我使用表达式搜索成千上万行(使用上面的findall表达式)

    3 个答案:

    答案 0 :(得分:3)

    一种简单的方法是使用它:

    FFD8FF(?:[^F]|F(?!FD8FF))*FFD9
    

    说明:

    FFD8FF
    (?:     # this group describe the allowed content between the "anchors" 
        [^F]        # all that is not a "F"
      |             # OR
        F(?!FD8FF)  # a "F" not followed by "FD8FF"
    )*              # repeat (greedy)
    FFD9            # until the last FFD9 before FFD8FF
    

    即使贪婪量词用于该组,正则表达式引擎也会回溯以找到最后一个“FFD9”子字符串。

    如果要确保存在FFD8FF,可以在模式的末尾添加前瞻:

    FFD8FF(?:[^F]|F(?!FD8FF))*FFD9(?=.*?FFD8FF)
    

    您可以通过模拟一个限制回溯的原子组来优化此模式,并允许在组内使用量词:

    FFD8FF(?:(?=([^F]+|F(?!FD8FF)))\1)*FFD9
    

    这个技巧使用了这样一个事实:一旦前括号到达,前瞻的内容自然是原子的。因此,如果将一个组封装在一个内部具有捕获组的前瞻中,则只需要在后面放置后向引用以获得“原子”(不可分割的子串)。 当正则表达式引擎需要回溯时,它将逐个原子地回溯,而不是逐字符地逐字回溯。

    如果在此技巧之前需要捕获组,请不要忘记更新反向引用的数量,例如:

    (FFD8FF(?:(?=([^F]+|F(?!FD8FF)))\2)*FFD9)
    
    (FFD8FF((?:(?=([^F]+|F(?!FD8FF)))\3)*)FFD9)
    

    工作示例:

    >>> import re
    >>> yourstr = 'asdfasdfasasdaFFD8FFasdfalsjdflajsdfljasdfasdfasdfasdfFFD9asdflasdflasdfFFD9asdfasdfFFD8FFasdfalsjdflajsdfljasdfasdfasdfasdfFFD9'
    >>> p = re.compile(r'(FFD8FF((?:(?=([^F]+|F(?!FD8FF)))\3)*)FFD9)(?=.*?FFD8FF)')
    >>> re.findall(p, yourstr)
    [('FFD8FFasdfalsjdflajsdfljasdfasdfasdfasdfFFD9asdflasdflasdfFFD9', 'asdfalsjdflajsdfljasdfasdfasdfasdfFFD9asdflasdflasdf', 'D9asdflasdflasdf')]
    

    变体:

    (FFD8FF((?:(?=(F(?!FD8FF)[^F]*|[^F]+))\3)*)FFD9)(?=.*?FFD8FF)
    

    答案 1 :(得分:1)

    由于您的应用程序架构不限于一个正则表达式,因此将其分解为几个步骤:

    1. 您希望以每个FFD8FF开头的单位分解文本。只需使用在下一个FFD8FFre.findall(r"FFD8FF.*?(?=FFD8FF)", contents)之前结束的非贪婪搜索。 (这使用了预测,在我看来过度使用;但它可以让你为下一个字符串保存最终的FFD8FF。)

    2. 然后,您需要修剪每个此类字符串,使其在 last FFD9处结束。最简单的方法是使用贪婪的搜索:re.search(r"^.*FFD9", part)。像这样:

      for part in re.findall(r"FFD8FF.*?(?=FFD8FF)", contents):
          print(re.search(r"^.*FFD9", part).group(0))
      
    3. 简单,可维护和高效。

    答案 2 :(得分:0)

    我就是这样做的:

    >>> re.search(r'((FFD8FF).+?(FFD9))(?:((?!FFD9).)+FFD8FF)', s).groups()
    ('FFD8FFasdfalsjdflajsdfljasdfasdfasdfasdfFFD9asdflasdflasdfFFD9',
     'FFD8FF',
     'FFD9',
     'f')
    

    第二部分只搜索不包含以FFD9结尾的FFD8FF的字符串。

    它包含您的搜索组件,因此您仍然可以在正则表达式中替换它们。但是对于像这样复杂的东西,我会避免使用正则表达式。

    不过,感谢您发布高质量的正则表达式问题,而不是通常的垃圾邮件。