如何有效地匹配Perl正则表达式中已匹配的内容?

时间:2012-08-09 22:17:52

标签: regex perl lookahead lookaround

我写了一个正则表达式来验证必须符合以下规则的字符串:

  1. 必须至少有一个字符
  2. 必须不包含空白字符
  3. 第一个字符可能不是标点符号
  4. 最后一封信可能不是标点符号
  5. 可能不会以标点符号后跟数字
  6. 结束
  7. /[:@#]以外的所有其他字符可以是任何UTF-8字符。
  8. 这是正则表达式:

    my $name_re = qr/
         [^[:punct:][:blank:]]      #  not punct or blank
         (?:                        #  followed by...
             [^[:blank:]:@#]*       #      any number non-blank, non-@, non-#, non-@
             [^[:punct:][:blank:]]  #      one not blank or punct
         )?                         #  ... optionally
    /x;
    

    看到什么遗失?规则#5未得到强制执行。我一直在通过编写如下代码来强制执行它:

    die "$proj is not a valid name" unless $proj =~ /\A$name_re\z/
        && $proj !~ /[[:punct:]][[:digit:]]+\z/;
    

    我必须有一个的地方,所以我宁愿这一切都在一个正则表达式中完成。问题是:如何?正则表达式会拒绝诸如“foo,23”之类的值?

2 个答案:

答案 0 :(得分:3)

以下内容应该有效:

my $name_re = qr/
    \A(?![[:punct:]])         # first character isn't punctuation
    (?:                       # start non-capturing group, repeated once or more
       (?![[:punct:]][[:digit:]]+\z)  # make sure 5th condition isn't violated
       [^[:blank:]:@#]                # match a valid character
    )+                        # end non-capturing group
    (?<![[:punct:]])\z        # last character isn't punctuation
/x;

请注意,我将锚点移动到正则表达式中,这对于您当前的方法可能不是完全必要的,但我认为将它们放在一个地方会更清楚。

(?!...)(?<!...)分别是负面的前瞻和后瞻。他们可以很简单地验证这样的事情,基本上中间部分可以“匹配这些有效字符”,在开头和结尾都有前瞻/后视来检查这些条件。

中间的负向前瞻验证在给定位置,我们无法仅使用标点符号或数字匹配字符串的结尾,或者换句话说,它会检查以确保不违反条件5。因为这个前瞻是在重复的组内,所以在每个位置进行检查。

如果你可以使用可变长度的lookbehind,这会更简单,但我认为Perl不支持它们。

答案 1 :(得分:0)

@ f-j的答案对于匹配完整的字符串是正确的,但是还需要一个可以作为较大字符串的一部分与其中的其他内容匹配的变体。这是那个版本:

my $name_re = qr/
    (?![[:punct:]])                # first character isn't punctuation
    (?:                            # start non-capturing group, repeated once or more ...
       (?!                         #    negative look ahead for...
           [[:punct:]]             #       punctuation
           [[:digit:]]+            #       digits
           (?:$|[[:blank:]])       #       eol or blank
       )                           #    ...
       [^[:blank:]:@#]             #     match a valid character
    )+                             # ... end non-capturing group
    (?<![[:punct:]])\b             # last character isn't punctuation
/x;