2012-08-09 62 views
1

我寫了一個正則表達式來驗證必須遵循以下規則的字符串:如何有效地匹配Perl正則表達式中已匹配的內容?

  1. 必須至少有一個字符
  2. 必須不包含空格字符
  3. 第一個字符可能不標點符號
  4. 最後一個字母可能不是標點符號
  5. 可能不會以標點符號後面跟數字結尾
  6. 其他所有字符都可以是除之外的任何UTF-8字符。

這裏是正則表達式:

my $name_re = qr/ 
    [^[:punct:][:blank:]]  # not punct or blank 
    (?:      # followed by... 
     [^[:blank:]:@#]*  #  any number non-blank, [email protected], non-#, [email protected] 
     [^[: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」之類的值?

回答

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支持它們。

+0

這並不禁止字符串以標點加上任何數字,即'/ [[:punct:]] [[:digit:]] * /'。後面的部分必須是固定寬度的,所以'*'不能使用。 :-( – theory 2012-08-09 22:32:16

+0

此發出 「有效」,而應發出 「無效」:'perl的-E「說 「富,23」=〜/ \ A(?![:PUNCT:])(?:([?! PUNCT:] \ d + $)[^ [:空白:]:@#])+(<[:PUNCT:])\ Z/「有效」?!? 「無效」'' – theory 2012-08-09 22:34:53

+0

@theory - 有在我的後面沒有重複,我做了一些小的編輯,但它總是檢查標點符號和數字在結尾處(我只將'\ d'切換到'[:digit:]'並將$前綴更改爲'\ z')。 – 2012-08-09 22:36:13

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;