1

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

  1. 必须至少为一个字符
  2. 不得包含空白字符
  3. 第一个字符不能是标点符号
  4. 最后一个字母不能是标点符号
  5. 不能以标点符号结尾,后跟数字
  6. 所有其他字符可以是除/[:@#].

这是正则表达式:

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”之类的值?

4

2 回答 2

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。因为这个前瞻在重复组内,所以在每个位置都检查它。

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

于 2012-08-09T22:27:43.860 回答
0

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

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;
于 2012-08-17T05:19:39.080 回答