Asp.net

使用 ASP.NET 3.5 驗證的電子郵件格式驗證的最佳正則表達式

  • December 29, 2020

我已使用以下兩個正則表達式來測試具有 ASP.NET 驗證控制項的有效電子郵件表達式。我想知道從性能的角度來看哪種表達方式更好,或者是否有人有更好的表達方式。

- \w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
- ^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[ 0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$

我正在嘗試避免BCL 團隊部落格上描述的“指數緩慢表達”問題。

更新

根據回饋,我最終創建了一個函式來測試電子郵件是否有效:

Public Function IsValidEmail(ByVal emailString As String, Optional ByVal isRequired As Boolean = False) As Boolean
   Dim emailSplit As String()
   Dim isValid As Boolean = True
   Dim localPart As String = String.Empty
   Dim domainPart As String = String.Empty
   Dim domainSplit As String()
   Dim tld As String

   If emailString.Length >= 80 Then
       isValid = False
   ElseIf emailString.Length > 0 And emailString.Length < 6 Then
       'Email is too short
       isValid = False
   ElseIf emailString.Length > 0 Then
       'Email is optional, only test value if provided
       emailSplit = emailString.Split(CChar("@"))

       If emailSplit.Count <> 2 Then
           'Only 1 @ should exist
           isValid = False
       Else
           localPart = emailSplit(0)
           domainPart = emailSplit(1)
       End If

       If isValid = False OrElse domainPart.Contains(".") = False Then
           'Needs at least 1 period after @
           isValid = False
       Else
           'Test Local-Part Length and Characters
           If localPart.Length > 64 OrElse ValidateString(localPart, ValidateTests.EmailLocalPartSafeChars) = False OrElse _
              localPart.StartsWith(".") OrElse localPart.EndsWith(".") OrElse localPart.Contains("..") Then
               isValid = False
           End If

           'Validate Domain Name Portion of email address
           If isValid = False OrElse _
              ValidateString(domainPart, ValidateTests.HostNameChars) = False OrElse _
              domainPart.StartsWith("-") OrElse domainPart.StartsWith(".") OrElse domainPart.Contains("..") Then
               isValid = False
           Else
               domainSplit = domainPart.Split(CChar("."))
               tld = domainSplit(UBound(domainSplit))

               ' Top Level Domains must be at least two characters
               If tld.Length < 2 Then
                   isValid = False
               End If
           End If
       End If
   Else
       'If no value is passed review if required
       If isRequired = True Then
           isValid = False
       Else
           isValid = True
       End If
   End If

   Return isValid
End Function

筆記:

  • IsValidEmail 對允許的字元比 RFC 更嚴格,但它不會測試這些字元的所有可能的無效使用

如果您想知道為什麼這個問題產生的活動如此之少,那是因為在您開始考慮性能之前還有很多其他問題需要處理。其中最重要的是您是否應該使用正則表達式來驗證電子郵件地址——而共識是您不應該這樣做。這比大多數人預期的要棘手得多,而且可能毫無意義。

另一個問題是您的兩個正則表達式在它們可以匹配的字元串類型上差別很大。例如,第二個錨定在兩端,但第一個沒有;它將匹配“ >>>>foo@bar.com<<<<”,因為其中嵌入了一些看起來像電子郵件地址的東西。也許框架會強制正則表達式匹配整個字元串,但如果是這種情況,為什麼要錨定第二個?

另一個區別是第一個正則表達式在所有地方都使用\w,而第二個[0-9a-zA-Z]在很多地方都使用。在大多數正則表達式風格中,\w除了字母和數字之外,還匹配下劃線,但在某些(包括 .NET)中,它還匹配來自 Unicode 已知的每個書寫系統的字母和數字。

還有許多其他差異,但那是學術上的;這些正則表達式都不是很好。有關該主題的良好討論以及更好的正則表達式,請參見此處。

回到最初的問題,我沒有看到這兩個正則表達式的性能問題。除了該 BCL 部落格條目中引用的嵌套量詞反模式之外,您還應該注意正則表達式的兩個或多個相鄰部分可以匹配同一組字元的情況——例如,

([A-Za-z]+|\w+)@

您發布的任何一個正則表達式中都沒有類似的東西。由量詞控制的部分總是被其他未被量化的部分分解。兩個正則表達式都會經歷一些可以避免的回溯,但是有很多比性能更好的理由來拒絕它們。

編輯:所以第二個正則表達式遭受災難性的回溯;我應該在開槍之前徹底測試它。仔細看看那個正則表達式,我不明白為什麼你需要在第一部分使用外部星號:

[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*

該位所做的只是確保第一個和最後一個字元是字母數字,同時允許在它們之間添加一些額外的字元。這個版本做同樣的事情,但是當無法匹配時它會更快地失敗:

[0-9a-zA-Z][-.\w]*[0-9a-zA-Z]

這可能足以消除回溯問題,但您也可以通過使用原子組使“@”之後的部分更有效:

(?>(?:[0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+)[a-zA-Z]{2,9}

換句話說,如果您已經將所有看起來像域組件的子字元串與尾隨點相匹配,並且下一部分看起來不像 TLD,則不要費心回溯。你必須放棄的第一個字元是最後一個點,你知道[a-zA-Z]{2,9}不會匹配。

引用自:https://stackoverflow.com/questions/936870