如何确认字符串是有效的电子邮件格式How to verify that strings are in valid email format

下面的示例使用正则表达式来验证一个字符串是否为有效的电子邮件格式。The following example uses a regular expression to verify that a string is in valid email format.

示例Example

该示例定义 IsValidEmail 方法,如果字符串包含有效的电子邮件地址,则该方法返回 true ,否则返回 false ,但不采取其他任何操作。The example defines an IsValidEmail method, which returns true if the string contains a valid email address and false if it does not, but takes no other action.

若要验证电子邮件地址是否有效,方法 IsValidEmail 将使用 Regex.Replace(String, String, MatchEvaluator) 正则表达式模式调用 (@)(.+)$ 方法将域名从电子邮件地址分离。To verify that the email address is valid, the IsValidEmail method calls the Regex.Replace(String, String, MatchEvaluator) method with the (@)(.+)$ regular expression pattern to separate the domain name from the email address. 第三个参数是表示了处理和替换匹配文本的方法的 MatchEvaluator 委托。The third parameter is a MatchEvaluator delegate that represents the method that processes and replaces the matched text. 正则表达式模式的解释如下。The regular expression pattern is interpreted as follows.

模式Pattern 描述Description
(@) 匹配 @ 字符。Match the @ character. 这是第一个捕获组。This is the first capturing group.
(.+) 匹配任意字符的一个或多个匹配项。Match one or more occurrences of any character. 这是第二个捕获组。This is the second capturing group.
$ 在字符串的结尾结束匹配。End the match at the end of the string.

使用 @ 字符的域名已传递给 DomainMapper 方法,该方法使用 IdnMapping 类将 US-ASCII 字符范围外的 Unicode 字符转换为 Punycode。The domain name along with the @ character is passed to the DomainMapper method, which uses the IdnMapping class to translate Unicode characters that are outside the US-ASCII character range to Punycode. 如果 invalid 方法在域名中检测到任何无效字符,该方法还会将 True 标志设置为 IdnMapping.GetAsciiThe method also sets the invalid flag to True if the IdnMapping.GetAscii method detects any invalid characters in the domain name. 该方法将冠以 @ 符号的 Punycode 域名返回给 IsValidEmail 方法。The method returns the Punycode domain name preceded by the @ symbol to the IsValidEmail method.

然后 IsValidEmail 方法调用 Regex.IsMatch(String, String) 方法验证该地址是否符合正则表达式模式。The IsValidEmail method then calls the Regex.IsMatch(String, String) method to verify that the address conforms to a regular expression pattern.

请注意, IsValidEmail 方法不执行身份验证来验证电子邮件地址。Note that the IsValidEmail method does not perform authentication to validate the email address. 它只确定其格式对于电子邮件地址是否有效。It merely determines whether its format is valid for an email address. 此外, IsValidEmail 方法不对顶级域名是否是 IANA 根区域数据库中列出的有效域名进行验证,这需执行查找操作。In addition, the IsValidEmail method does not verify that the top-level domain name is a valid domain name listed at the IANA Root Zone Database, which would require a look-up operation. 相反,正则表达式仅验证由二到二十四个 ASCII 字符组成的顶级域名,该域名以字母数字开头并以字符结尾,且其余的字符是字母数字或连字符 (-)。Instead, the regular expression merely verifies that the top-level domain name consists of between two and twenty-four ASCII characters, with alphanumeric first and last characters and the remaining characters being either alphanumeric or a hyphen (-).

using System;
using System.Globalization;
using System.Text.RegularExpressions;

public class RegexUtilities
{
    public static bool IsValidEmail(string email)
    {
        if (string.IsNullOrWhiteSpace(email))
            return false;

        try
        {
            // Normalize the domain
            email = Regex.Replace(email, @"(@)(.+)$", DomainMapper,
                                  RegexOptions.None, TimeSpan.FromMilliseconds(200));

            // Examines the domain part of the email and normalizes it.
            string DomainMapper(Match match)
            {
                // Use IdnMapping class to convert Unicode domain names.
                var idn = new IdnMapping();

                // Pull out and process domain name (throws ArgumentException on invalid)
                var domainName = idn.GetAscii(match.Groups[2].Value);

                return match.Groups[1].Value + domainName;
            }
        }
        catch (RegexMatchTimeoutException e)
        {
            return false;
        }
        catch (ArgumentException e)
        {
            return false;
        }

        try
        {
            return Regex.IsMatch(email,
                @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
                @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
                RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
        }
        catch (RegexMatchTimeoutException)
        {
            return false;
        }
    }
}
Imports System.Globalization
Imports System.Text.RegularExpressions

Public Class RegexUtilities

    Public Shared Function IsValidEmail(email As String) As Boolean

        If String.IsNullOrWhiteSpace(email) Then Return False

        ' Use IdnMapping class to convert Unicode domain names.
        Try
            'Examines the domain part of the email and normalizes it.
            Dim DomainMapper =
                Function(match As Match) As String

                    'Use IdnMapping class to convert Unicode domain names.
                    Dim idn = New IdnMapping

                    'Pull out and process domain name (throws ArgumentException on invalid)
                    Dim domainName As String = idn.GetAscii(match.Groups(2).Value)

                    Return match.Groups(1).Value & domainName
                    
                End Function

            'Normalize the domain
            email = Regex.Replace(email, "(@)(.+)$", DomainMapper,
                                  RegexOptions.None, TimeSpan.FromMilliseconds(200))

        Catch e As RegexMatchTimeoutException
            Return False

        Catch e As ArgumentException
            Return False

        End Try

        Try
            Return Regex.IsMatch(email,
                                 "^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
                                 "(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
                                 RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250))

        Catch e As RegexMatchTimeoutException
            Return False

        End Try

    End Function

End Class

在本例中,正则表达式模式 ^(?(")(".+?(?<!\\)"@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$ 的解释如以下图例所示。In this example, the regular expression pattern ^(?(")(".+?(?<!\\)"@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$ is interpreted as shown in the following legend. 使用 RegexOptions.IgnoreCase 标志编译正则表达式。The regular expression is compiled using the RegexOptions.IgnoreCase flag.

模式 ^:从字符串的开头部分开始匹配。Pattern ^: Begin the match at the start of the string.

模式 (?("):确定第一个字符是否为引号。Pattern (?("): Determine whether the first character is a quotation mark. (?(") 为替换构造的开头。(?(") is the beginning of an alternation construct.

模式 (?(")(".+?(?<!\\)"@):如果第一个字符是引号,则匹配一个开始引号,后跟至少一个任意字符,再后跟一个结束引号。Pattern (?(")(".+?(?<!\\)"@): If the first character is a quotation mark, match a beginning quotation mark followed by at least one occurrence of any character, followed by an ending quotation mark. 不得在结束引号前面加反斜杠字符 (\)。The ending quotation mark must not be preceded by a backslash character (\). (?<! 是零宽度负预测先行断言的开头。(?<! is the beginning of a zero-width negative lookbehind assertion. 字符串应以 at 符号 (@) 结束。The string should conclude with an at sign (@).

模式 |(([0-9a-z]:如果第一个字符不是引号,则匹配从 a 到 z 或 A 到 Z(比较不区分大小写)的任意字母字符或从 0 到 9 的任意数字字符。Pattern |(([0-9a-z]: If the first character is not a quotation mark, match any alphabetic character from a to z or A to Z (the comparison is case insensitive), or any numeric character from 0 to 9.

模式 (\.(?!\.)):如果下一个字符为句点,则匹配它。Pattern (\.(?!\.)): If the next character is a period, match it. 如果下一个字符不为句点,则看下一个字符并继续进行匹配。If it is not a period, look ahead to the next character and continue the match. (?!\.) 是宽度为零的负预测先行断言,可防止两个连续句号出现在电子邮件地址的本地部分中。(?!\.) is a zero-width negative lookahead assertion that prevents two consecutive periods from appearing in the local part of an email address.

模式 |[-!#\$%&'\*\+/=\?\^`\{\}\|~\w]:如果下一个字符不为句点,则匹配任何单词字符或以下字符之一:-!#$%&'*+/=?^`{}|~Pattern |[-!#\$%&'\*\+/=\?\^`\{\}\|~\w]: If the next character is not a period, match any word character or one of the following characters: -!#$%&'*+/=?^`{}|~

模式 ((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*:匹配替换模式(一个句点,后跟一个非句点或许多字符中的某个字符)零次或多次。Pattern ((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*: Match the alternation pattern (a period followed by a non-period, or one of a number of characters) zero or more times.

模式 @:匹配 @ 字符。Pattern @: Match the @ character.

模式 (?<=[0-9a-z]):如果 @ 字符之前的字符为从 A 到 Z、从 a 到 z 或从 0 到 9 的字符,则继续进行匹配。Pattern (?<=[0-9a-z]): Continue the match if the character that precedes the @ character is A through Z, a through z, or 0 through 9. 此模式定义了一个零宽度的正后行断言。This pattern defines a zero-width positive lookbehind assertion.

模式 (?(\[):检查 @ 后面的字符是否为左括号。Pattern (?(\[): Check whether the character that follows @ is an opening bracket.

模式 (\[(\d{1,3}\.){3}\d{1,3}\]):如果该字符为左括号,则匹配该左括号,后跟 IP 地址(四个数字组,每个数字组包含一到三位数字,并且每个数字组用句点隔开)和右括号。Pattern (\[(\d{1,3}\.){3}\d{1,3}\]): If it is an opening bracket, match the opening bracket followed by an IP address (four sets of one to three digits, with each set separated by a period) and a closing bracket.

模式 |(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+:如果 @ 后面的字符不是左括号,匹配一个字母数字字符(A-Z、a-z 或 0-9 中的某个值),后跟零个或多个连字符,再后跟零个或一个字母数字字符(A-Z、a-z 或 0-9 中的某个值),再后跟一个句点。Pattern |(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+: If the character that follows @ is not an opening bracket, match one alphanumeric character with a value of A-Z, a-z, or 0-9, followed by zero or more occurrences of a hyphen, followed by zero or one alphanumeric character with a value of A-Z, a-z, or 0-9, followed by a period. 此模式可以重复一次或多次,并且必须后跟顶级域名。This pattern can be repeated one or more times, and must be followed by the top-level domain name.

模式 [a-z0-9][\-a-z0-9]{0,22}[a-z0-9])):顶级域名必须以字母数字字符(a-z、A-Z 和 0-9)开始和结束。Pattern [a-z0-9][\-a-z0-9]{0,22}[a-z0-9])): The top-level domain name must begin and end with an alphanumeric character (a-z, A-Z, and 0-9). 它还可以包括从零到 22 个字母数字或连字符的 ASCII 字符。It can also include from zero to 22 ASCII characters that are either alphanumeric or hyphens.

模式 $:在字符串的结尾结束匹配。Pattern $: End the match at the end of the string.

编译代码Compile the code

IsValidEmail 方法和 DomainMapper 方法可以包括在正则表达式实用工具方法库中,或者作为私有静态或实例方法包括在应用程序类中。The IsValidEmail and DomainMapper methods can be included in a library of regular expression utility methods, or they can be included as private static or instance methods in the application class.

还可以使用 Regex.CompileToAssembly 方法将此正则表达式包含到正则表达式库中。You can also use the Regex.CompileToAssembly method to include this regular expression in a regular expression library.

如果在正则表达式库中对其进行使用,则可使用例如以下代码对其进行调用:If they are used in a regular expression library, you can call them by using code such as the following:

class Program
{
    static void Main(string[] args)
    {
        string[] emailAddresses = { "david.jones@proseware.com", "d.j@server1.proseware.com",
                                    "jones@ms1.proseware.com", "j.@server1.proseware.com",
                                    "j@proseware.com9", "js#internal@proseware.com",
                                    "j_9@[129.126.118.1]", "j..s@proseware.com",
                                    "js*@proseware.com", "js@proseware..com",
                                    "js@proseware.com9", "j.s@server1.proseware.com",
                                    "\"j\\\"s\\\"\"@proseware.com", "js@contoso.中国" };

        foreach (var emailAddress in emailAddresses)
        {
            if (RegexUtilities.IsValidEmail(emailAddress))
                Console.WriteLine($"Valid:   {emailAddress}");
            else
                Console.WriteLine($"Invalid: {emailAddress}");
        }

        Console.ReadKey();
    }
}
// The example displays the following output:
//       Valid: david.jones@proseware.com
//       Valid: d.j@server1.proseware.com
//       Valid: jones@ms1.proseware.com
//       Invalid: j.@server1.proseware.com
//       Valid: j@proseware.com9
//       Valid: js#internal@proseware.com
//       Valid: j_9@[129.126.118.1]
//       Invalid: j..s@proseware.com
//       Invalid: js*@proseware.com
//       Invalid: js@proseware..com
//       Valid: js@proseware.com9
//       Valid: j.s@server1.proseware.com
//       Valid: "j\"s\""@proseware.com
//       Valid: js@contoso.中国
Public Class Application
   Public Shared Sub Main()
      Dim emailAddresses() As String = {"david.jones@proseware.com", "d.j@server1.proseware.com",
                                       "jones@ms1.proseware.com", "j.@server1.proseware.com",
                                       "j@proseware.com9", "js#internal@proseware.com",
                                       "j_9@[129.126.118.1]", "j..s@proseware.com",
                                       "js*@proseware.com", "js@proseware..com",
                                       "js@proseware.com9", "j.s@server1.proseware.com",
                                       """j\""s\""""@proseware.com", "js@contoso.中国"}

      For Each emailAddress As String In emailAddresses
         If RegexUtilities.IsValidEmail(emailAddress) Then
               Console.WriteLine($"Valid:   {emailAddress}")
         Else
               Console.WriteLine($"Invalid: {emailAddress}")
         End If
      Next
   End Sub
End Class
' The example displays the following output:
'       Valid: david.jones@proseware.com
'       Valid: d.j@server1.proseware.com
'       Valid: jones@ms1.proseware.com
'       Invalid: j.@server1.proseware.com
'       Valid: j@proseware.com9
'       Valid: js#internal@proseware.com
'       Valid: j_9@[129.126.118.1]
'       Invalid: j..s@proseware.com
'       Invalid: js*@proseware.com
'       Invalid: js@proseware..com
'       Valid: js@proseware.com9
'       Valid: j.s@server1.proseware.com
'       Valid: "j\"s\""@proseware.com
'       Valid: js@contoso.中国

请参阅See also