后向模式无效

时间:2019-07-18 21:38:13

标签: python regex ruby regex-lookarounds

为什么此正则表达式在Python中有效,但在Ruby中无效:

/(?<!([0-1\b][0-9]|[2][0-3]))/

很高兴听到一个解释以及如何在Ruby中解决它

编辑带有整行代码:

re.sub(r'(?<!([0-1\b][0-9]|[2][0-3])):(?!([0-5][0-9])((?i)(am)|(pm)|(a\.m)|(p\.m)|(a\.m\.)|(p\.m\.))?\b)' , ':\n' , s)

基本上,我试图在没有冒号的情况下添加'\n'

3 个答案:

答案 0 :(得分:1)

Ruby正则表达式引擎不允许捕获后面的组。 如果您需要分组,则可以使用非捕获组(?:)

[8] pry(main)> /(?<!(:?[0-1\b][0-9]|[2][0-3]))/
SyntaxError: (eval):2: invalid pattern in look-behind: /(?<!(:?[0-1\b][0-9]|[2][0-3]))/
[8] pry(main)> /(?<!(?:[0-1\b][0-9]|[2][0-3]))/
=> /(?<!(?:[0-1\b][0-9]|[2][0-3]))/

Docs:

 (?<!subexp)        negative look-behind

                     Subexp of look-behind must be fixed-width.
                     But top-level alternatives can be of various lengths.
                     ex. (?<=a|bc) is OK. (?<=aaa(?:b|cd)) is not allowed.

                     In negative look-behind, capturing group isn't allowed,
                     but non-capturing group (?:) is allowed.

this answer那里学习。

答案 1 :(得分:1)

肯定@mrzasa找出了问题所在。

但是.. 猜测您打算用':\ n`
替换非时间冒号 我猜可以这样做。也会修剪一些空白。

(?i)(?<!\b[01][0-9])(?<!\b[2][0-3])([^\S\r\n]*:)[^\S\r\n]*(?![0-5][0-9](?:[ap]\.?m\b\.?)?)

PCRE-https://regex101.com/r/7TxbAJ/1替换$1\n

Python-https://regex101.com/r/w0oqdZ/1替换\1\n

可读版本

 (?i)
 (?<!
      \b [01] [0-9] 
 )
 (?<!
      \b [2] [0-3] 
 )
 (                             # (1 start)
      [^\S\r\n]* 
      :
 )                             # (1 end)
 [^\S\r\n]* 
 (?!
      [0-5] [0-9] 
      (?: [ap] \.? m \b \.? )?
 )

答案 2 :(得分:0)

访问到Onigmo regex documentation,否定式后备支持不支持捕获组。尽管在正则表达式引擎中很常见,但并非所有人都将其视为错误,因此您会看到re和Onigmo正则表达式库中的差异。

现在,对于您的正则表达式来说,它不能正常工作,在Ruby和Python中都无法正常工作:PythonRuby的正则表达式中的字符类中的\b与BACKSPACE匹配( \x08)字符,而不是单词边界。此外,当您在可选的非单词char后面使用单词边界时,如果char出现在字符串中,则必须立即在该非单词char的右侧出现单词char。字边界必须移至m之前\.?之后。

当前方法的另一个缺陷是,回退并不是最好排除某些特定上下文(如此处)的方法。例如。您不能在时间数字和am / pm之间计入可变数量的空格。最好匹配您不想触摸的上下文,并匹配并捕获要修改的上下文。因此,我们在这里需要两个主要选择,一个在时间字符串中匹配am / pm,另一个在所有其他上下文中匹配它们。

您的模式也有太多可供选择的选择,可以使用字符类和?量词进行合并。

Regex demo

  • \b((?:[01]?[0-9]|2[0-3]):[0-5][0-9]\s*[pa]\.?m\b\.?)
    • \b-单词边界
    • ((?:[01]?[0-9]|2[0-3]):[0-5][0-9]\s*[pa]\.?m\b\.?)-捕获组1:
      • (?:[01]?[0-9]|2[0-3])-可选的01,然后是任意数字或2,然后是从03的数字
      • :[0-5][0-9]-:,然后是从0059的数字
      • \s*-超过0个空格
      • [pa]\.?m\b\.?-ap,可选点,m单词边界,可选点
  • |-或
  • \b[ap]\.?m\b\.?-单词边界ap,可选点,m单词边界,可选点

Python fixed solution

import re
text = 'am pm  P.M.  10:56pm 10:43 a.m.'
rx = r'\b((?:[01]?[0-9]|2[0-3]):[0-5][0-9]\s*[pa]\.?m\b\.?)|\b[ap]\.?m\b\.?'
result = re.sub(rx, lambda x: x.group(1) if x.group(1) else "\n", text, flags=re.I)

Ruby solution

text = 'am pm  P.M.  10:56pm 10:43 a.m.'
rx = /\b((?:[01]?[0-9]|2[0-3]):[0-5][0-9]\s*[pa]\.?m\b\.?)|\b[ap]\.?m\b\.?/i
result = text.gsub(rx) { $1 || "\n" }

输出:

"\n \n  \n  10:56pm 10:43 a.m."