【问题标题】:Can a perl-compatible regex compare two numbers?兼容 perl 的正则表达式可以比较两个数字吗?
【发布时间】:2016-04-22 17:43:06
【问题描述】:

定位本杰明巴顿

给定...

Born,Died
1852,1891
1862,1862
1902,1785

..,在 perl 兼容的正则表达式中是否有一种语法将匹配第四行,其中第一个值大于第二个值?

我的猜测是结合...

(\d+),(\d+)

...和...

(??{$1>$2})

..,但也许不可能,因为正则表达式是词法的,而匹配是算术的。

编辑:这仅限于 pcre-regex,因为环境接受 pcre 模式但禁止 perl 程序。

【问题讨论】:

  • 澄清一下,您要的是 Perl 正则表达式还是 PCRE 正则表达式? PCRE doesn't support the ??{code} syntax.
  • 另外,在没有正则表达式的 Perl 中这很简单(并且更容易阅读):while (<$fh>) { chomp; my ($born, $died) = split /,/; print if $died < $born; } 你有什么理由必须使用正则表达式吗?
  • @ThisSuitIsBlackNot,我个人对 ??{code} 的语法很好奇,但手头的问题需要 PCRE。根据您提供的链接,手头的问题是没有希望的;除非 pcre-callout 功能可能是一个桥梁。
  • 现在是一个答案,但很酷的正则表达式魔法:perl -le '$x = "123,456"; print $x =~ /(\d),([\1-9])/; print $1; print $2;' - 只要您处理 1 位数字,正则表达式就可以完成您的工作。
  • this regex 呢?

标签: regex pcre


【解决方案1】:

使用 Perl“延迟执行断言”(??{code}) 的工作模式是:

^(\d{4}),(\d{4})(??{ $1 > $2 ? "" : "(?!)"})$

延迟执行断言将代码返回的值放入正则表达式。在这种情况下,如果第一个数字大于第二个数字(因此匹配),则模式变为^(\d{4}),(\d{4})$,否则为^(\d{4}),(\d{4})(?!)$(?!) 是一个从不匹配的否定前瞻断言,因为 Perl 认为空模式总是匹配的。

Perl 中的另一个选项是使用“条件表达式”(?(condition)yes-pattern) 和“代码评估断言”(?{code})

^(\d{4}),(\d{4})(?(?{ $1 <= $2 })(?!))$

如果第一个数字小于或等于第二个数字,这具有将永远不匹配的(?!) 添加到模式的效果。我的测试表明这比上面的第一个模式要快得多。

有关所有 Perl 正则表达式功能的详细信息,请参阅 perlre 手册页。

请参阅 Jeff Pinyan 撰写的 Regex Arcana 文章,了解有关 (??{code})(?{code}) 模式的优秀教程。

但是,上述模式不适用于 PCRE 库。 @Sebastian 的评论提出了一个可能的解决方案(不起作用):

^(\d*)(\d)\d*,\1[^\D\2-9]\d*$

这会尝试查找数字对,其中数字具有相同的前缀并且第二个数字中的第一个不同字符不是非数字(即它一个数字)并且不等于或大于第一个数字中的相应数字(即它小于另一个数字)。不幸的是,它不起作用。原因在General approach for (equivalent of) “backreferences within character class”? 中有解释。基本上,反向引用在字符类中不起作用。这个想法可以通过使用延迟执行断言 (^(\d*)(\d)\d*,\1(??{"[^\D${2}-9]"})\d*$) 来实现,但这对 PCRE 来说仍然没有好处。

一个与 PCRE 兼容的选项是使用检查第一个不同数字想法的蛮力版本。寻找 1 后跟 0,或 2 后跟 0 或 1,或 3 后跟 0 或 1 或 2,...。这个 Bash 代码的 sn-p 生成正则表达式:

regex='^(?=\d{4},\d{4}$)'   # Match only lines of the form 'dddd,dddd'
regex+='(\d*)'              # Prefix of both numbers
regex+='(1\d*,\1[0]'        # 1 (followed by digits+','+prefix) followed by 0
for (( i=2 ; i<=9 ; i++ )) ; do
    regex+="|$i\d*,\1[0-$((i-1))]"  # or $i (...) followed by a lesser digit
done
regex+=')\d*$'
printf '%s\n' "$regex"

它还在@Denomales 在第一个发布的答案中使用的正则表达式的开头添加了相同的正面预测断言。生成的正则表达式为:

^(?=\d{4},\d{4}$)(\d*)(1\d*,\1[0]|2\d*,\1[0-1]|3\d*,\1[0-2]|4\d*,\1[0-3]|5\d*,\1[0-4]|6\d*,\1[0-5]|7\d*,\1[0-6]|8\d*,\1[0-7]|9\d*,\1[0-8])\d*$

正如@ThisSuitIsBlackNot 在评论中指出的那样,正则表达式并不是最好的方法。另见What is meant by “Now you have two problems”?

【讨论】:

    【解决方案2】:

    总结

    此正则表达式假定您的源号码是 4 位字符串。它将查找第一个逗号分隔的数字在数值上大于第二个数字的实例。
    如所写,此正则表达式假定您正在使用忽略空格或换行符的“x”标志。

    正则表达式

    ^(?=\d{4},\d{4}(?:\D|\Z))(?:(?:
    [9]\d*,[012345678]\d*|
    [89]\d*,[01234567]\d*|
    [789]\d*,[0123456]\d*|
    [6789]\d*,[012345]\d*|
    [56789]\d*,[01234]\d*|
    [456789]\d*,[0123]\d*|
    [3456789]\d*,[012]\d*|
    [23456789]\d*,[01]\d*|
    [123456789]\d*,[0]\d*
    )|
    (?<a>\d{1})(?:
    [9]\d*,\k<a>[012345678]\d*|
    [89]\d*,\k<a>[01234567]\d*|
    [789]\d*,\k<a>[0123456]\d*|
    [6789]\d*,\k<a>[012345]\d*|
    [56789]\d*,\k<a>[01234]\d*|
    [456789]\d*,\k<a>[0123]\d*|
    [3456789]\d*,\k<a>[012]\d*|
    [23456789]\d*,\k<a>[01]\d*|
    [123456789]\d*,\k<a>[0]\d*
    )|
    (?<b>\d{2})(?:
    [9]\d*,\k<b>[012345678]\d*|
    [89]\d*,\k<b>[01234567]\d*|
    [789]\d*,\k<b>[0123456]\d*|
    [6789]\d*,\k<b>[012345]\d*|
    [56789]\d*,\k<b>[01234]\d*|
    [456789]\d*,\k<b>[0123]\d*|
    [3456789]\d*,\k<b>[012]\d*|
    [23456789]\d*,\k<b>[01]\d*|
    [123456789]\d*,\k<b>[0]\d*
    )|
    (?<c>\d{3})(?:
    [9]\d*,\k<c>[012345678]\d*|
    [89]\d*,\k<c>[01234567]\d*|
    [789]\d*,\k<c>[0123456]\d*|
    [6789]\d*,\k<c>[012345]\d*|
    [56789]\d*,\k<c>[01234]\d*|
    [456789]\d*,\k<c>[0123]\d*|
    [3456789]\d*,\k<c>[012]\d*|
    [23456789]\d*,\k<c>[01]\d*|
    [123456789]\d*,\k<c>[0]\d*
    ))
    

    示例

    http://www.rubular.com/r/XjBNBQIzGP

    示例文本

    Born,Died
    1852,1891
    1862,1862
    1902,1785
    1111,1111
    1111,1110
    2222,2202
    3333,3033
    4444,0444
    123,456
    1234,567
    123,4567
    456,123
    4567,123
    456,1234
    4567,1234
    

    样本捕获

    [0][0] = 1902,1785
    [0][a] = 1
    [0][b] = 
    [0][c] = 
    [1][0] = 1111,1110
    [1][a] = 
    [1][b] = 
    [1][c] = 111
    [2][0] = 2222,2202
    [2][a] = 
    [2][b] = 22
    [2][c] = 
    [3][0] = 3333,3033
    [3][a] = 3
    [3][b] = 
    [3][c] = 
    [4][0] = 4444,0444
    [4][a] = 
    [4][b] = 
    [4][c] = 
    [5][0] = 4567,1234
    [5][a] = 
    [5][b] = 
    [5][c] = 
    

    简短说明

    正则表达式的开头有一个前瞻来验证我们确实有两个 4 位数字。

    四个代码块测试每个位置以验证一个数字是否大于另一个数字。第二个、第三个和第四个块包含一个命名的反向引用(分别为 a、b、c)。此反向引用确保前导数字相同。

    更详细的解释

    NODE                     EXPLANATION
    ----------------------------------------------------------------------
      ^                        the beginning of a "line"
    ----------------------------------------------------------------------
      (?=                      look ahead to see if there is:
    ----------------------------------------------------------------------
        \d{4}                    digits (0-9) (4 times)
    ----------------------------------------------------------------------
        ,                        ','
    ----------------------------------------------------------------------
        \d{4}                    digits (0-9) (4 times)
    ----------------------------------------------------------------------
        (?:                      group, but do not capture:
    ----------------------------------------------------------------------
          \D                       non-digits (all but 0-9)
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          \Z                       before an optional \n, and the end of
                                   the string
    ----------------------------------------------------------------------
        )                        end of grouping
    ----------------------------------------------------------------------
      )                        end of look-ahead
    ----------------------------------------------------------------------
      (?:                      group, but do not capture:
    ----------------------------------------------------------------------
        (?:                      group, but do not capture:
    ----------------------------------------------------------------------
          [9]                      any character of: '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [012345678]              any character of: '0', '1', '2', '3',
                                   '4', '5', '6', '7', '8'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [89]                     any character of: '8', '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [01234567]               any character of: '0', '1', '2', '3',
                                   '4', '5', '6', '7'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [789]                    any character of: '7', '8', '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [0123456]                any character of: '0', '1', '2', '3',
                                   '4', '5', '6'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [6789]                   any character of: '6', '7', '8', '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [012345]                 any character of: '0', '1', '2', '3',
                                   '4', '5'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [56789]                  any character of: '5', '6', '7', '8',
                                   '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [01234]                  any character of: '0', '1', '2', '3',
                                   '4'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [456789]                 any character of: '4', '5', '6', '7',
                                   '8', '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [0123]                   any character of: '0', '1', '2', '3'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [3456789]                any character of: '3', '4', '5', '6',
                                   '7', '8', '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [012]                    any character of: '0', '1', '2'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [23456789]               any character of: '2', '3', '4', '5',
                                   '6', '7', '8', '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [01]                     any character of: '0', '1'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
         |                        OR
    ----------------------------------------------------------------------
          [123456789]              any character of: '1', '2', '3', '4',
                                   '5', '6', '7', '8', '9'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
          ,                        ','
    ----------------------------------------------------------------------
          [0]                      any character of: '0'
    ----------------------------------------------------------------------
          \d*                      digits (0-9) (0 or more times
                                   (matching the most amount possible))
    ----------------------------------------------------------------------
        )                        end of grouping
    ----------------------------------------------------------------------
       |                        OR
    ----------------------------------------------------------------------
      )                        end of grouping
    ----------------------------------------------------------------------
    )                        end of grouping
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-08
      • 1970-01-01
      相关资源
      最近更新 更多