使用ASP.NET 3.5验证进行电子邮件格式验证的最佳正则表达式

Jos*_*osh 7 regex asp.net

我使用以下两个正则表达式来测试带有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团队博客上描述的"指数缓慢表达"问题.

UPDATE

根据反馈,我最终创建了一个函数来测试电子邮件是否有效:

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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 对于允许使用RFC的字符,IsValidEmail更具限制性,但它不会测试这些字符的所有可能的无效使用

Ala*_*ore 12

如果你想知道为什么这个问题产生如此少的活动,那是因为在你开始考虑性能之前还有许多其他问题需要处理.其中最重要的是你是否应该使用正则表达式来验证电子邮件地址 - 而且你不应该达成共识.它比大多数人想象的要复杂得多,而且无论如何都可能毫无意义.

另一个问题是你的两个正则表达式在它们可以匹配的字符串类型上有很大差异.例如,第二个锚定在两端,但第一个不是; 它会匹配" >>>>foo@bar.com<<<<",因为它看起来像嵌入其中的电子邮件地址.也许框架强制正则表达式匹配整个字符串,但如果是这样的话,为什么第二个锚定?

另一个区别是第一个正则表达式\w始终使用,而第二个正则表达式用于[0-9a-zA-Z]许多地方.在大多数正则表达式中,\w除了字母和数字之外还匹配下划线,但在某些(包括.NET)中,它还匹配来自Unicode已知的每个书写系统的字母和数字.

还有很多其他的差异,但这是学术上的; 这些正则表达式都不是很好.请参阅此处以获得有关该主题的更好讨论,以及更好的正则表达式.

回到最初的问题,我没有看到这些正则表达式的性能问题.除了BCL博客条目中引用的嵌套量词反模式之外,您还应该注意正则表达式的两个或多个相邻部分可以匹配同一组字符的情况 - 例如,

([A-Za-z]+|\w+)@
Run Code Online (Sandbox Code Playgroud)

在你发布的任何一个正则表达式中没有类似的东西.由量词控制的零件总是被其他未量化的零件分解.两个正则表达式都会经历一些可避免的回溯,但有很多比性能更好的理由拒绝它们.

编辑:所以第二个正则表达式受到灾难性的回溯; 在拍摄我的嘴之前,我应该彻底测试它.仔细看看那个正则表达式,我不明白为什么你需要在第一部分中使用外部星号:

[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*
Run Code Online (Sandbox Code Playgroud)

所有这一切都确保第一个和最后一个字符是字母数字,同时允许其间有一些额外的字符.这个版本做了同样的事情,但是当不可能匹配时它会更快地失败:

[0-9a-zA-Z][-.\w]*[0-9a-zA-Z]
Run Code Online (Sandbox Code Playgroud)

这可能足以消除回溯问题,但你也可以通过使用原子组使"@"之后的部分更有效:

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

换句话说,如果你已经匹配所有可能的带有尾随点的域组件的子串,并且下一部分看起来不像TLD,则不要打扰回溯.你必须放弃的第一个角色是最后一个点,你知道它[a-zA-Z]{2,9}不会匹配.


Dav*_*ave 8

我们使用此RegEx,已经在内部测试了150万个地址.它正确地识别出超过98%的我们的,但有些格式我知道它会出错.

^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$
Run Code Online (Sandbox Code Playgroud)

我们还确保数据中没有EOL字符,因为EOL可以伪造此RegEx.我们的职责:

Public Function IsValidEmail(ByVal strEmail As String) As Boolean
    ' Check An eMail Address To Ensure That It Is Valid
    Const cValidEmail = "^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$"   ' 98% Of All Valid eMail Addresses
    IsValidEmail = False
    ' Take Care Of Blanks, Nulls & EOLs
    strEmail = Replace(Replace(Trim$(strEmail & " "), vbCr, ""), vbLf, "")
    ' Blank eMail Is Invalid
    If strEmail = "" Then Exit Function
    ' RegEx Test The eMail Address
    Dim regEx As New System.Text.RegularExpressions.Regex(cValidEmail)
    IsValidEmail = regEx.IsMatch(strEmail)
End Function
Run Code Online (Sandbox Code Playgroud)