Làm thế nào để xác thực một địa chỉ email bằng cách sử dụng một biểu thức thông thường?


3312

Trong những năm qua, tôi đã dần dần phát triển một biểu thức chính quy xác thực các địa chỉ email MOST chính xác, giả sử họ không sử dụng địa chỉ IP làm phần máy chủ.

Tôi sử dụng nó trong một số chương trình PHP và nó hoạt động hầu hết thời gian. Tuy nhiên, thỉnh thoảng tôi được liên lạc bởi một người gặp sự cố với trang web sử dụng nó và cuối cùng tôi phải điều chỉnh (gần đây nhất tôi nhận ra rằng tôi không cho phép TLD 4 ký tự).

Biểu thức chính quy tốt nhất bạn có hoặc đã thấy để xác thực email là gì?

Tôi đã thấy một số giải pháp sử dụng các hàm sử dụng một số biểu thức ngắn hơn, nhưng tôi muốn có một biểu thức phức tạp dài trong một hàm đơn giản thay vì một vài biểu thức ngắn trong một hàm phức tạp hơn.



5
Regex có thể xác nhận rằng IDNA được định dạng chính xác không phù hợp với stackexchange. (các quy tắc về chuẩn hóa đã ăn thực sự quanh co và đặc biệt không phù hợp với xử lý regex)
Jasen


Các biểu thức chính có thể thay đổi vì trong một số trường hợp, một email con có thể chứa một khoảng trắng và trong các thời điểm khác, nó không thể chứa bất kỳ khoảng trắng nào.
Ṃųỻịgǻňạc thiết ơ

Câu trả lời:


2439

Các đầy đủ RFC 822 compliant regex là không hiệu quả và tối nghĩa vì chiều dài của nó. May mắn thay, RFC 822 đã được thay thế hai lần và thông số kỹ thuật hiện tại cho các địa chỉ email là RFC 5322 . RFC 5322 dẫn đến một regex có thể được hiểu nếu được nghiên cứu trong vài phút và đủ hiệu quả để sử dụng thực tế.

Có thể tìm thấy một regex tuân thủ RFC 5322 ở đầu trang tại http://emailregex.com/ nhưng sử dụng mẫu địa chỉ IP trôi nổi trên internet với một lỗi cho phép 00bất kỳ giá trị thập phân byte không dấu nào trong một địa chỉ được phân định bằng dấu chấm, đó là bất hợp pháp. Phần còn lại của nó có vẻ phù hợp với ngữ pháp RFC 5322 và vượt qua một số bài kiểm tra bằng cách sử dụng grep -Po, bao gồm cả tên miền, địa chỉ IP, tên xấu và tên tài khoản có và không có dấu ngoặc kép.

Sửa 00lỗi trong mẫu IP, chúng tôi có được một regex hoạt động và khá nhanh. (Quét phiên bản được hiển thị, không phải đánh dấu, cho mã thực tế.)

(?: [a-z0-9! # $% & '* + / =? ^ _ `{|} ~ -] + (?: \. [a-z0-9! # $% &' * + / =? ^ _ `{|} ~ -] +) * |" (?: [\ x01- \ x08 \ x0b \ x0c \ x0e- \ x1f \ x21 \ x23- \ x5b \ x5d- \ x7f] | \\ [\ x01- \ x09 \ x0b \ x0c \ x0e- \ x7f]) * ") @ (?: (?: [a-z0-9] (?: [a-z0-9 -] * [a-z0 -9])? \.) + [A-z0-9] (?: [A-z0-9 -] * [a-z0-9])? | \ [(? :(? :( 2 (5 [0-5] | [0-4] [0-9]) | 1 [0-9] [0-9] | [1-9]? [0-9])) \.) {3} ( ? :( 2 (5 [0-5] | [0-4] [0-9]) | 1 [0-9] [0-9] | [1-9]? [0-9]) | [ a-z0-9 -] * [a-z0-9]: (?: [\ x01- \ x08 \ x0b \ x0c \ x0e- \ x1f \ x21- \ x5a \ x53- \ x7f] | \ [\ x01- \ x09 \ x0b \ x0c \ x0e- \ x7f]) +) \])

hoặc là:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

Dưới đây là sơ đồ của máy trạng thái hữu hạn cho regrec ở trên, rõ ràng hơn chính regrec nhập mô tả hình ảnh ở đây

Các mẫu phức tạp hơn trong Perl và PCRE (thư viện regex được sử dụng, ví dụ như trong PHP) có thể phân tích chính xác RFC 5322 mà không gặp trở ngại nào . Python và C # cũng có thể làm điều đó, nhưng chúng sử dụng một cú pháp khác với hai cái đầu tiên. Tuy nhiên, nếu bạn buộc phải sử dụng một trong nhiều ngôn ngữ phù hợp với mẫu kém mạnh mẽ hơn, thì tốt nhất là sử dụng trình phân tích cú pháp thực sự.

Điều quan trọng là phải hiểu rằng việc xác thực nó theo RFC cho bạn hoàn toàn không biết gì về việc địa chỉ đó có thực sự tồn tại ở miền được cung cấp hay không, hoặc người nhập địa chỉ có phải là chủ sở hữu thực sự hay không. Mọi người đăng ký người khác để gửi danh sách theo cách này mọi lúc. Việc sửa lỗi yêu cầu một loại xác thực fancier liên quan đến việc gửi địa chỉ đó một tin nhắn bao gồm mã thông báo xác nhận có nghĩa được nhập trên cùng một trang web như địa chỉ.

Mã xác nhận là cách duy nhất để biết bạn có địa chỉ của người nhập. Đây là lý do tại sao hầu hết các danh sách gửi thư hiện nay sử dụng cơ chế đó để xác nhận đăng ký. Rốt cuộc, bất kỳ ai cũng có thể đặt xuống president@whitehouse.gov, và điều đó thậm chí sẽ phân tích thành hợp pháp, nhưng nó không có khả năng là người ở đầu kia.

Đối với PHP, bạn không nên sử dụng mẫu được đưa ra trong Xác thực địa chỉ E-mail với PHP, đúng cách mà tôi trích dẫn:

Có một số nguy hiểm rằng việc sử dụng phổ biến và mã hóa cẩu thả trên diện rộng sẽ thiết lập một tiêu chuẩn thực tế cho các địa chỉ email hạn chế hơn so với tiêu chuẩn chính thức được ghi lại.

Điều đó không tốt hơn tất cả các mẫu phi RFC khác. Nó thậm chí không đủ thông minh để xử lý ngay cả RFC 822 , chứ chưa nói RFC 5322. Cái này , tuy nhiên, là.

Nếu bạn muốn nhận được ưa thích và pedantic, thực hiện một công cụ nhà nước hoàn chỉnh . Một biểu thức chính quy chỉ có thể hoạt động như một bộ lọc thô sơ. Vấn đề với các biểu thức thông thường là việc nói với ai đó rằng địa chỉ email hoàn toàn hợp lệ của họ là không hợp lệ (dương tính giả) vì biểu thức thông thường của bạn không thể xử lý nó chỉ là thô lỗ và bất lịch sự từ quan điểm của người dùng. Một công cụ trạng thái cho mục đích có thể xác nhận và thậm chí chính xác các địa chỉ e-mail mà nếu không được coi là không hợp lệ vì nó phân tách địa chỉ e-mail theo từng RFC. Điều này cho phép trải nghiệm có khả năng làm hài lòng hơn, như

Địa chỉ email được chỉ định 'myemail @ address, com' không hợp lệ. Ý bạn là 'myemail@address.com'?

Xem thêm Xác thực địa chỉ email , bao gồm cả các ý kiến. Hoặc So sánh Địa chỉ E-mail Xác thực Biểu thức thông thường .

Hình dung biểu thức thường xuyên

Trình diễn gỡ lỗi


179
Bạn nói "Không có biểu hiện thường xuyên tốt." Đây có phải là chung hoặc cụ thể để xác nhận địa chỉ e-mail?
Tomalak

37
@Tomalak: chỉ dành cho địa chỉ email. Như bortzmeyer đã nói, RFC cực kỳ phức tạp
Luk

37
Bài báo tạp chí linux mà bạn đề cập là thực tế sai ở một số khía cạnh. Cụ thể Lovell rõ ràng đã không đọc errata cho RFC3696 và lặp lại một số lỗi trong phiên bản RFC đã xuất bản. Xem thêm tại đây: dominicsayers.com/isemail
Dominic Sayers

9
Jeff Atwood có regex đáng yêu trong bài viết trên blog này để xác nhận tất cả các địa chỉ email hợp lệ: codinghorror.com/blog/2005/02/regex-use-vs-regex-abuse.html
CMircea

5
Lưu ý rằng thông số HTML5 hiện tại bao gồm regex và ABNF để xác thực đầu vào kiểu email bị hạn chế nhiều hơn so với RFC ban đầu.
Synchro

746

Bạn không nên sử dụng biểu thức thông thường để xác thực địa chỉ email.

Thay vào đó, hãy sử dụng lớp MailAddress , như thế này:

try {
    address = new MailAddress(address).Address;
} catch(FormatException) {
    // address is invalid
}

Các MailAddresslớp học sử dụng một phân tích cú pháp BNF để xác nhận địa chỉ phù hợp hoàn toàn với RFC822.

Nếu bạn dự định sử dụng MailAddressđể xác thực địa chỉ e-mail, hãy lưu ý rằng phương pháp này cũng chấp nhận phần tên hiển thị của địa chỉ e-mail và đó có thể không chính xác là những gì bạn muốn đạt được. Ví dụ: nó chấp nhận các chuỗi này là địa chỉ email hợp lệ:

  • "user1@hotmail.com; user2 @ gmail"
  • "user1@hotmail.com; user2 @ gmail; user3@company.com"
  • "Tên hiển thị người dùng user3@company.com"
  • "user4 @ company.com"

Trong một số trường hợp này, chỉ phần cuối của chuỗi được phân tích cú pháp làm địa chỉ; phần còn lại trước đó là tên hiển thị. Để có được một địa chỉ email đơn giản mà không có bất kỳ tên hiển thị nào, bạn có thể kiểm tra địa chỉ được chuẩn hóa theo chuỗi gốc của bạn.

bool isValid = false;

try
{
    MailAddress address = new MailAddress(emailAddress);
    isValid = (address.Address == emailAddress);
    // or
    // isValid = string.IsNullOrEmpty(address.DisplayName);
}
catch (FormatException)
{
    // address is invalid
}

Hơn nữa, một địa chỉ có một dấu chấm ở cuối, như user@company. được MailAddress chấp nhận.

Nếu bạn thực sự muốn sử dụng regex, đây là :

(?: (?: \ r \ n)? [\ t]) * (?: (?: (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031 ] + (?: (?: (?: \ R \ n)? [\ T]
) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | "(?: [^ \" \ R \\] | \\. | (?: (?: \ R \ n)? [\ T])) * "(? :(?:
\ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ \ ". \ [\] \ 000- \ 031] + (? :(? :(
?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | "(?: [ ^ \ "\ r \\] | \\. | (?: (?: \ R \ n)? [ 
\ t])) * "(?: (?: \ r \ n)? [\ t]) *)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 0
31] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\ ]])) | \ [([^ \ [\] \ r \\] | \\.) * \
] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ T]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] +
(?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]]) ) | \ [([^ \ [\] \ r \\] | \\.) * \] (?:
(?: \ r \ n)? [\ t]) *)) * | (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z
| (? = [\ ["() <> @,;: \". \ [\]])) | "(?: [^ \" \ r \\] | \\. | (? :( ?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)
? [\ t]) *) * \ <(?: (?: \ r \ n)? [\ t]) * (?: @ (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \
r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\ ] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [
 \ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)
? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ r \ \] | \\.) * \] (?: (?: \ R \ n)? [\ T]
) *)) * (?:, @ (?: (?: \ r \ n)? [\ T]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [
 \ t]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ r \\] | \.) * \] (?: (?: \ R \ n)? [\ T]) *
) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031 ] + (?: (?: (?: \ R \ n)? [\ T]
) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ R \\] | \ .) * \] (?: (?: \ r \ n)? [\ t]) *)) *)
*: (?: (?: \ R \ n)? [\ T]) *)? (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) +
| \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | "(?: [^ \" \ R \\] | \\. | ( ?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r
\ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ " . \ [\] \ 000- \ 031] + (? :(? :(?:
\ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | "(?: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t
))) * "(?: (?: \ R \ n)? [\ T]) *)) * @ (?: (?: \ R \ n)? [\ T]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031
] + (?: (?: (?: \ R \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\] ])) | \ [([^ \ [\] \ r \\] | \\.) * \] (
?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?
: (?: (?: \ r \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (? :(?
: \ r \ n)? [\ t]) *)) * \> (?: (?: \ r \ n)? [\ t]) *) | (?: [^ () <> @,; : \ ". \ [\] \ 000- \ 031] + (? :(?
: (?: \ r \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | "(? : [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)?
[\ t])) * "(?: (?: \ r \ n)? [\ t]) *) *: (?: (?: \ r \ n)? [\ t]) * (?: (?: (?: [^ () <> @,;: \ ". \ [\] 
\ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \" . \ [\]])) | "(?: [^ \" \ r \\] |
\\. | (?: (?: \ r \ n)? [\ T])) * "(?: (?: \ r \ n)? [\ t]) *) (?: \. (? : (?: \ r \ n)? [\ t]) * (?: [^ () <>

@,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ [ "() <> @,;: \". \ [\]])) | "
(?: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) *" (?: (?: \ r \ n)? [ \ t]) *)) * @ (?: (?: \ r \ n)? [\ t]
) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \
". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) * ) (?: \. (?: (?: \ r \ n)? [\ t]) * (?
: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \". \ [
\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ R \ n)? [\ T]) *)) * | (?: [^ () <> @,;: \ ". \ [\] \ 000-
\ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \". \ [ \]])) | "(?: [^ \" \ r \\] | \\. | (
?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t]) *) * \ <(?: (?: \ r \ n)? [\ t]) * (?: @ (?: [^ () <> @,;
: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ [" () <> @,;: \ ". \ [\]])) | \ [([
^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ "
. \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @, ;: \ ". \ [\]])) | \ [([^ \ [\
] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * (?:, @ (?: (?: \ r \ n )? [\ t]) * (?: [^ () <> @,;: \ ". \
[\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \ ". \ [\]])) | \ [([^ \ [\] \
r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] 
\ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \" . \ [\]])) | \ [([^ \ [\] \ r \\]
| \.) * \] (?: (?: \ R \ n)? [\ T]) *)) *) *: (?: (?: \ R \ n)? [\ T]) * )? (?: [^ () <> @,;: \ ". \ [\] \ 0
00- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | "(?: [^ \" \ r \\] | \\
. | (?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t]) *) (?: \. (? :( ?: \ r \ n)? [\ t]) * (?: [^ () <> @,
;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ [" ( ) <> @,;: \ ". \ [\]])) |" (?
: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) *" (?: (?: \ r \ n)? [\ t ]) *)) * @ (?: (?: \ R \ n)? [\ T]) *
(?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \".
\ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ R \ n)? [\ t]) *) ( ?: \. (?: (?: \ r \ n)? [\ t]) * (?: [
^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | ( ? = [\ ["() <> @,;: \". \ [\]
))) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ R \ n)? [\ T]) *)) * \> ( ?: (?: \ r \ n)? [\ t]) *) (?:, \ s * (
?: (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \
". \ [\]])) |" (?: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) *" (? : (?: \ r \ n)? [\ t]) *) (?: \. (? :(
?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (? :(? :(? : \ r \ n)? [\ t]) + | \ Z | (? = [
\ ["() <> @,;: \". \ [\]])) | "(?: [^ \" \ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t
]) *)) * @ (?: (?: \ R \ n)? [\ T]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T
]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ R \\] | \ \.) * \] (?: (?: \ R \ n)? [\ T]) *) (?
: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + ( ?: (?: (?: \ r \ n)? [\ t]) + |
\ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ R \\] | \\.) * \] (?: (?: \ R \ n)? [\ T]) *)) * | (?:
[^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \". \ [\
]])) | "(?: [^ \" \ R \\] | \\. | (?: (?: \ R \ n)? [\ T])) * "(?: (?: \ r \ n)? [\ t]) *) * \ <(?: (?: \ r \ n)
? [\ t]) * (?: @ (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["
() <> @,;: \ ". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)
? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <>

@,;: \ ". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * (?:, @ (?: (?: \ r \ n)? [
 \ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,
;: \ ". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ R \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]
) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \
". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) * )) *) *: (?: (?: \ R \ n)? [\ T]) *)?
(?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \".
\ [\]])) | "(?: [^ \" \ r \\] | \\. | (?: (?: \ r \ n)? [\ T])) * "(? :( ?: \ r \ n)? [\ t]) *) (?: \. (? :(?:
\ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ [
"() <> @,;: \". \ [\]])) | "(?: [^ \" \ r \\] | \\. | (?: (?: \ R \ n) ? [\ t])) * "(?: (?: \ r \ n)? [\ t])
*)) * @ (?: (?: \ R \ n)? [\ T]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t])
+ | \ Z | (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ R \\] | \\. ) * \] (?: (?: \ R \ n)? [\ t]) *) (?: \
. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z
| (? = [\ ["() <> @,;: \". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * \> (? :(
?: \ r \ n)? [\ t]) *)) *) ?; \ s *)

26
Bạn sẽ thấy rằng lớp MailAddress trong .NET 4.0 tốt hơn nhiều trong việc xác thực địa chỉ email so với các phiên bản trước. Tôi đã thực hiện một số cải tiến đáng kể cho nó.
Jeff Tucker

7
Tôi nghĩ rằng nó ... không hoạt động ... cho các id đơn giản hơn. a @ b không xác nhận. ar@b.com chỉ khớp với ar @ b, .com không khớp. Tuy nhiên, một cái gì đó như "Tôi là tôi" @ [10.10.10.10] không hoạt động! :)
Raze

5
Được cảnh báo rằng các trình xác thực regex tuân thủ RFC này sẽ cho phép rất nhiều địa chỉ email mà bạn có thể không muốn chấp nhận, chẳng hạn như "a body / onload = alert (' lol.com?'+document.cookies ) @aa> "đó là một địa chỉ email hợp lệ trong Email của Perl :: Hợp lệ (sử dụng regex lớn đó) và có thể được khai thác cho XSS rt.cpan.org/Public/Bug/Display.html?id=75650
Matthew Lock

9
@MatthewLock: Điều đó không tệ hơn fake@not-a-real-domain.name. Bạn không được dựa vào xác nhận email để ngăn XSS.
SLaks

10
@MatthewLock: Không. Bạn cần thoát các truy vấn SQL (hoặc, tốt hơn là sử dụng các tham số). Vệ sinh không phải là một phòng thủ thích hợp.
SLaks

536

Câu hỏi này được hỏi rất nhiều, nhưng tôi nghĩ bạn nên lùi lại và tự hỏi tại sao bạn muốn xác thực địa chỉ email theo cú pháp? Lợi ích thực sự là gì?

  • Nó sẽ không bắt lỗi chính tả.
  • Nó không ngăn mọi người nhập địa chỉ email không hợp lệ hoặc bịa đặt hoặc nhập địa chỉ của người khác.

Nếu bạn muốn xác thực rằng một email là chính xác, bạn không có lựa chọn nào hơn là gửi email xác nhận và yêu cầu người dùng trả lời. Trong nhiều trường hợp, dù sao bạn cũng sẽ phải gửi thư xác nhận vì lý do bảo mật hoặc vì lý do đạo đức (vì vậy bạn không thể đăng ký một dịch vụ nào đó trái với ý muốn của họ).


92
Có thể đáng để kiểm tra rằng họ đã nhập một cái gì đó @ một cái gì đó vào trường trong xác thực phía máy khách chỉ để bắt những lỗi đơn giản - nhưng nói chung bạn đã đúng.
Martin Beckett

8
Martin, tôi đã cho bạn +1, chỉ sau đó mới đọc rằng foobar @ dk là một email hợp lệ. Nó sẽ không đẹp, nhưng nếu bạn muốn cả hai đều tuân thủ RFC VÀ sử dụng thông thường, bạn nên phát hiện các trường hợp như thế này và yêu cầu người dùng xác nhận đó là chính xác.
philfreo

106
@olavk: nếu ai đó nhập lỗi chính tả (ví dụ me@hotmail:), rõ ràng họ sẽ không nhận được email xác nhận của bạn, và sau đó họ ở đâu? Họ không còn trên trang web của bạn nữa và họ tự hỏi tại sao họ không thể đăng ký. Trên thực tế không có họ - họ đã hoàn toàn quên bạn. Tuy nhiên, nếu bạn chỉ có thể thực hiện kiểm tra độ tỉnh táo cơ bản với regex trong khi họ vẫn ở bên bạn, thì họ có thể bắt lỗi ngay lập tức và bạn đã có một người dùng hạnh phúc.
nickf

5
@JacquesB: Bạn làm cho một điểm tuyệt vời. Chỉ vì nó vượt qua được cơ chế theo RFC không có nghĩa đó thực sự là địa chỉ của người dùng. Mặt khác, tất cả các president@whitehouse.govđịa chỉ đó chỉ ra một tổng chỉ huy rất netbusy. :)
tchrist

39
Nó không phải là màu đen hoặc trắng. Nếu e-mail có vẻ sai, hãy cho người dùng biết điều đó. Nếu người dùng vẫn muốn tiếp tục, hãy để anh ta. Đừng ép người dùng tuân thủ regex của bạn, thay vào đó, hãy sử dụng regex như một công cụ để giúp người dùng biết rằng có thể có lỗi.
ninjaneer

354

Tất cả phụ thuộc vào mức độ chính xác mà bạn muốn trở thành. Đối với mục đích của tôi, nơi tôi chỉ cố gắng tránh những thứ như bob @ aol.com(khoảng trắng trong email) hoặc steve(không có tên miền nào) hoặc mary@aolcom(không có thời gian trước .com), tôi sử dụng

/^\S+@\S+\.\S+$/

Chắc chắn, nó sẽ khớp với những thứ không phải là địa chỉ email hợp lệ, nhưng đó là vấn đề nhận được các lỗi đơn giản phổ biến.

Có bất kỳ số lượng thay đổi nào có thể được thực hiện cho biểu thức chính quy đó (và một số trong các nhận xét cho câu trả lời này), nhưng nó đơn giản và dễ hiểu, và là một nỗ lực đầu tiên tốt.


6
Nó không khớp với foobar @ dk, đây là một địa chỉ email hợp lệ và đang hoạt động (mặc dù có lẽ hầu hết các máy chủ thư sẽ không chấp nhận hoặc sẽ thêm một cái gì
đó.com

3
Nó sẽ được thôi. Tôi đề nghị bạn tự thử. $ perl -le'print q đũafoo@bar.co.uk} = ~ / ^ \ ^ + ^ \ S + \. \ IS + $ /? q {Y}: q {N} '
Andy Lester

7
@Richard: .được bao gồm trong \S.
David Thornley

43
JJJ: Vâng, nó sẽ phù hợp với rất nhiều chuyện tào lao. Nó sẽ khớp với & $ * # $ (@ $ 0 (%)) $ #.) & *) (* $, Đối với tôi, tôi quan tâm nhiều hơn đến việc bắt lỗi chính tả kỳ quặc như mary@aolcomtôi hoàn toàn là rác YMMV.
Andy Lester

5
Chỉ để kiểm soát các @dấu hiệu: /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/ jsfiddle.net/b9chris/mXB96
Chris Moschini

338

Nó phụ thuộc vào ý của bạn là gì tốt nhất: Nếu bạn đang nói về việc nắm bắt mọi địa chỉ email hợp lệ, hãy sử dụng như sau:

(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:
\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(
?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ 
\t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\0
31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\
](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+
(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:
(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)
?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\
r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[
 \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)
?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]
)*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[
 \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*
)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)
*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+
|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r
\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t
]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031
]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](
?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?
:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?
:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?
:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?
[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|
\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>
@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"
(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?
:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[
\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-
\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(
?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;
:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([
^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\"
.\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\
]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\
r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]
|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \0
00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\
.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,
;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?
:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[
^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]
]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(
?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(
?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[
\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t
])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t
])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?
:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|
\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:
[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\
]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)
?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["
()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)
?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>
@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[
 \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,
;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:
\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[
"()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])
*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])
+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\
.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(
?:\r\n)?[ \t])*))*)?;\s*)

( http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html ) Nếu bạn đang tìm kiếm thứ gì đó đơn giản hơn nhưng sẽ bắt được hầu hết các địa chỉ email hợp lệ, hãy thử một số thứ như:

"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"

EDIT: Từ liên kết:

Biểu thức chính quy này sẽ chỉ xác nhận các địa chỉ đã có bất kỳ nhận xét nào bị tước và thay thế bằng khoảng trắng (điều này được thực hiện bởi mô-đun).


10
Nó không khớp với tất cả các địa chỉ, một số phải được chuyển đổi trước. Từ liên kết: "Biểu thức chính quy này sẽ chỉ xác nhận các địa chỉ đã có bất kỳ nhận xét nào bị tước và thay thế bằng khoảng trắng (điều này được thực hiện bởi mô-đun)."
Chas. Owens

47
Bạn có thể cho tôi một ví dụ về một số email addresssai lầm đi qua cái thứ hai, nhưng bị bắt bởi regex dài hơn?
Lazer

4
Mặc dù tôi đã từng yêu thích nó, nhưng đó là một trình xác nhận RFC 822, không phải là RFC 5322 .
tchrist

24
@Lazer in..valid @ example.com sẽ là một ví dụ đơn giản. Bạn không được phép có hai dấu chấm không trích dẫn liên tiếp trong phần cục bộ.
Randal Schwartz

5
@Mikhail perl nhưng bạn thực sự không nên sử dụng nó.
Người tốt

287

[CẬP NHẬT] Tôi đã đối chiếu mọi thứ tôi biết về xác thực địa chỉ email tại đây: http://isemail.info , giờ đây không chỉ xác thực mà còn chẩn đoán sự cố với địa chỉ email. Tôi đồng ý với nhiều ý kiến ​​ở đây rằng xác nhận chỉ là một phần của câu trả lời; xem bài luận của tôi tại http://isemail.info/about .

is_email () vẫn còn, theo như tôi biết, trình xác nhận duy nhất sẽ cho bạn biết chắc chắn liệu một chuỗi đã cho có phải là địa chỉ email hợp lệ hay không. Tôi đã tải lên một phiên bản mới tại http://isemail.info/

Tôi đã đối chiếu các trường hợp thử nghiệm từ Cal Henderson, Dave Child, Phil Haack, Doug Lovell, RFC5322 và RFC 3696. Có tất cả 275 địa chỉ kiểm tra. Tôi đã chạy tất cả các bài kiểm tra này chống lại tất cả các trình xác nhận miễn phí mà tôi có thể tìm thấy.

Tôi sẽ cố gắng cập nhật trang này khi mọi người nâng cao trình xác nhận của họ. Cảm ơn Cal, Michael, Dave, Paul và Phil vì sự giúp đỡ và hợp tác của họ trong việc biên soạn các bài kiểm tra này và những lời chỉ trích mang tính xây dựng của người xác nhận của riêng tôi .

Mọi người nên biết về lỗi sai đối với RFC 3696 nói riêng. Ba trong số các ví dụ kinh điển trên thực tế là các địa chỉ không hợp lệ. Và độ dài tối đa của một địa chỉ là 254 hoặc 256 ký tự, không phải 320.


Trình xác nhận này cũng có vẻ đúng. [... thời gian trôi qua ...] Hừm, có vẻ như đó chỉ là RFC 5322, không phải 3693 hay errata.
tchrist

1
Rất đẹp. Ở đây chúng tôi không chỉ có được một bài luận hay, chúng tôi còn có một trình kiểm tra xác nhận cũng như thư viện để tải xuống. Câu trả lời tốt đẹp!
bgmCoder

Trình xác nhận của bạn không hỗ trợ Punycode (RFC 3492). name@öäü.at có thể là một địa chỉ hợp lệ. (nó được dịch thành name@xn--4ca9at.at)
Josef nói Phục hồi Monica

Xin chào @Josef. Bạn nên cố gắng xác nhận name@xn--4ca9at.atvì mã này là về xác nhận, không phải là giải thích. Nếu bạn muốn thêm một dịch giả chơi chữ, thì tôi rất vui khi chấp nhận yêu cầu kéo tại github.com/dominicsayers/isemail
Dominic Sayers

266

Theo thông số HTML5 của W3C :

^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$

Bối cảnh:

Một địa chỉ e-mail hợp lệ là một chuỗi phù hợp với sản xuất ABNF [...].

Lưu ý: Yêu cầu này là một vi phạm cố ý của RFC 5322 , trong đó xác định một cú pháp cho các địa chỉ e-mail mà là cùng một lúc quá khắt khe (trước khi “@” ký tự), quá mơ hồ (sau khi “@” ký tự), và quá lỏng lẻo ( cho phép nhận xét, ký tự khoảng trắng và chuỗi trích dẫn trong cách cư xử không quen thuộc với hầu hết người dùng) được sử dụng thực tế tại đây.

Biểu thức chính quy tương thích với JavaScript và Perl sau đây là cách triển khai định nghĩa trên.

/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/


12
Hay đấy. Đó là một sự vi phạm RFC, nhưng là một ý chí và nó làm cho vừng. Ví dụ trong thế giới thực: gmail bỏ qua các dấu chấm trong phần trước @, vì vậy nếu email của bạn là test @ gmail, bạn có thể gửi email để kiểm tra. @ Gmail.com hoặc kiểm tra .... @ gmail.com, cả hai địa chỉ đó đều là không hợp lệ theo RFC, nhưng hợp lệ trong thế giới thực.
valentina

Tôi nghĩ phần cuối phải là '+' thay vì '*': ^ [a-zA-Z0-9.! # $% & '* + / =? ^ _ `{|} ~ -] + @ [a- zA-Z0-9 -] + (?: \. [a-zA-Z0-9 -] +) + $
mmmmmm

7
@mmmmmm john.doe@localhostlà hợp lệ. Chắc chắn, trong một ứng dụng trong thế giới thực (ví dụ như một cộng đồng), tôi muốn đề xuất của bạn thay thế * bằng +
rabudde

3
@valentinas Trên thực tế, RFC không loại trừ các phần cục bộ này, nhưng chúng phải được trích dẫn. "test...."@gmail.comlà hoàn toàn hợp lệ theo RFC và tương đương về mặt ngữ nghĩa với test....@gmail.com.
Rinke

Tôi gặp lỗi trong khi cố gắng gửi email bằng python thông qua chuyển tiếp của công ty tôi nếu tôi cố gửi đến một địa chỉ có. @ Hoặc .. @. Trên thực tế đó cũng là trường hợp với một _ @. Tôi thà loại bỏ những thứ đó trước khi gửi hơn là tin tưởng rằng người nhận sẽ làm điều đó.
ndvo

201

Thật dễ dàng trong Perl 5.10 hoặc mới hơn:

/(?(DEFINE)
   (?<address>         (?&mailbox) | (?&group))
   (?<mailbox>         (?&name_addr) | (?&addr_spec))
   (?<name_addr>       (?&display_name)? (?&angle_addr))
   (?<angle_addr>      (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
   (?<group>           (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ;
                                          (?&CFWS)?)
   (?<display_name>    (?&phrase))
   (?<mailbox_list>    (?&mailbox) (?: , (?&mailbox))*)

   (?<addr_spec>       (?&local_part) \@ (?&domain))
   (?<local_part>      (?&dot_atom) | (?&quoted_string))
   (?<domain>          (?&dot_atom) | (?&domain_literal))
   (?<domain_literal>  (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                 \] (?&CFWS)?)
   (?<dcontent>        (?&dtext) | (?&quoted_pair))
   (?<dtext>           (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])

   (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
   (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
   (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
   (?<dot_atom_text>   (?&atext)+ (?: \. (?&atext)+)*)

   (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
   (?<quoted_pair>     \\ (?&text))

   (?<qtext>           (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
   (?<qcontent>        (?&qtext) | (?&quoted_pair))
   (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                        (?&FWS)? (?&DQUOTE) (?&CFWS)?)

   (?<word>            (?&atom) | (?&quoted_string))
   (?<phrase>          (?&word)+)

   # Folding white space
   (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
   (?<ctext>           (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
   (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
   (?<comment>         \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
   (?<CFWS>            (?: (?&FWS)? (?&comment))*
                       (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

   # No whitespace control
   (?<NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

   (?<ALPHA>           [A-Za-z])
   (?<DIGIT>           [0-9])
   (?<CRLF>            \x0d \x0a)
   (?<DQUOTE>          ")
   (?<WSP>             [\x20\x09])
 )

 (?&address)/x

20
Rất thích nhìn thấy điều này trong Python
tdc

4
Tôi nghĩ rằng chỉ có một tập hợp con của addrspecphần thực sự có liên quan đến câu hỏi. Chấp nhận nhiều hơn thế và chuyển tiếp nó mặc dù một số phần khác của hệ thống chưa sẵn sàng chấp nhận địa chỉ RFC5822 đầy đủ giống như chụp là chính đôi chân của bạn.
heo

3
Tuyệt vời (+1) nhưng về mặt kỹ thuật tất nhiên đó không phải là một biểu thức chính quy ... (điều này là không thể vì ngữ pháp không thường xuyên).
Rinke

10
regexes đã dừng lại thường xuyên một thời gian trước đây. Đó là một Perl 'regex' hợp lệ mặc dù!
rjh

4
Tôi đã thiết lập một thử nghiệm cho regex này trên IDEone: ideone.com/2XFecH Tuy nhiên, nó không công bằng "hoàn hảo". Bất cứ ai sẽ quan tâm để kêu vang trong? Tui bỏ lỡ điều gì vậy?
Mike

159

tôi sử dụng

^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

Cái nào được sử dụng trong ASP.NET bởi RoutExpressionValidator.


28
Boo! Địa chỉ (không thông báo) của !@mydomain.nettôi bị từ chối.
Phrogz

3
Theo trang này data.iana.org/TLD/tlds-alpha-by-domain.txt không có tên miền nào chỉ có một ký tự ở cấp cao nhất, ví dụ: " Something.c ", " Something.a ", đây là phiên bản hỗ trợ ít nhất 2 ký tự: "Something.pl", "Something.us":^\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w{2,}([-.]\\w+)*$
Tomasz Szulc

4
@Wayne Whitty. Bạn đã gặp phải vấn đề chính là có nên phục vụ cho phần lớn địa chỉ hay TẤT CẢ, bao gồm cả những địa chỉ không ai sử dụng, ngoại trừ để kiểm tra xác thực email.
Patanjali

@TomaszSzulc gạch chéo ngược trong câu trả lời của bạn thật khó hiểu, tôi chỉ sửa nó và 2 tên miền hỗ trợ tên miền đang hoạt động, ^ \ w + ([- +. '] \ W +) * @ \ w + ([-.] \ W +) * \. \ w {2,} ([-.] \ w +) * $
Aqib Mumtaz

2
điều này không simon-@hotmail.comthực sự hợp lệ (một khách hàng của chúng tôi có địa chỉ tương tự) `
Simon_Weaver

142

Không biết về điều tốt nhất, nhưng cái này ít nhất là chính xác, miễn là các địa chỉ có các bình luận của họ bị tước và thay thế bằng khoảng trắng.

Nghiêm túc. Bạn nên sử dụng một thư viện đã được viết để xác nhận email. Cách tốt nhất có lẽ là chỉ cần gửi e-mail xác minh đến địa chỉ đó.


2
Theo tôi biết, một số thư viện cũng sai. Tôi mơ hồ nhớ rằng PHP PEAR có một lỗi như vậy.
bortzmeyer

Trang đó cũng có phần từ chối ở phía dưới về một vài điều từ thông số kỹ thuật. rằng regrec không hỗ trợ.
Chris Vest

7
Đó là thông số RFC 822, không phải thông số RFC 5322 .
tchrist

12
Cuối cùng, anh ta đúng ở chỗ cách duy nhất để xác thực địa chỉ email là gửi email đến đó và chờ hồi âm.
Blazemonger

109

Các địa chỉ email tôi muốn xác thực sẽ được sử dụng bởi một ứng dụng web ASP.NET sử dụng không gian tên System.Net.Mail để gửi email đến danh sách mọi người. Vì vậy, thay vì sử dụng một số biểu thức chính quy rất phức tạp, tôi chỉ cố gắng tạo một cá thể MailAddress từ địa chỉ. Trình kết hợp MailAddress sẽ đưa ra một ngoại lệ nếu địa chỉ không được hình thành đúng. Bằng cách này, tôi biết tôi ít nhất có thể lấy email ra khỏi cửa. Tất nhiên đây là xác nhận phía máy chủ nhưng tối thiểu bạn vẫn cần điều đó.

protected void emailValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
    try
    {
        var a = new MailAddress(txtEmail.Text);
    }
    catch (Exception ex)
    {
        args.IsValid = false;
        emailValidator.ErrorMessage = "email: " + ex.Message;
    }
}

3
Một điểm tốt. Ngay cả khi xác thực máy chủ này từ chối một số địa chỉ hợp lệ thì đó không phải là vấn đề vì bạn sẽ không thể gửi đến địa chỉ này bằng công nghệ máy chủ cụ thể này. Hoặc bạn có thể thử làm những điều tương tự bằng bất kỳ thư viện gửi email của bên thứ ba nào bạn sử dụng thay vì các công cụ mặc định.
Người dùng

Tôi thực sự thích cách này tận dụng mã khung .Net - không có ý nghĩa trong việc phát minh lại bánh xe. Thật tuyệt vời. Đơn giản, sạch sẽ và đảm bảo bạn thực sự có thể gửi email. Công việc tuyệt vời
Nhà Cory

... Vâng và đối với những người quan tâm đến cách xác thực nó, hãy xem mã trong Reflector - có khá nhiều về nó - và đó không phải là biểu thức chính quy!
Tom Carter

2
Xin lưu ý: lớp MailAddress không khớp với RFC5322, nếu bạn chỉ muốn sử dụng nó để xác thực (và cũng không gửi, trong trường hợp đó là điểm tranh luận như đã đề cập ở trên). Xem: stackoverflow.com/questions/6023589/
Mạnh

Chỉ là một vấn đề nhỏ: nếu bạn muốn làm cho mã trình xác nhận phía máy chủ của mình có thể sử dụng lại nhiều hơn (trong trường hợp này hoặc nói chung), tôi khuyên bạn nên sử dụng args.Valuethay vì tham chiếu trường như txtEmail.Textmã hóa cứng. Cái sau sẽ ràng buộc trình xác nhận của bạn với trường hợp điều khiển duy nhất, điều đó có thể ổn, miễn là bạn có một trường e-mail duy nhất, nhưng không được đề xuất theo cách khác.
pholpar

109

Câu trả lời nhanh

Sử dụng biểu thức chính sau để xác thực đầu vào:

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)+

Địa chỉ phù hợp với regex này:

  • có một phần cục bộ (tức là phần trước @ -sign) tuân thủ nghiêm ngặt với RFC 5321/5322,
  • có một phần tên miền (tức là phần sau @ -sign) là tên máy chủ có ít nhất hai nhãn, mỗi nhãn dài tối đa 63 ký tự.

Hạn chế thứ hai là một hạn chế đối với RFC 5321/5322.

Xây dựng câu trả lời

Sử dụng biểu thức chính quy nhận dạng địa chỉ email có thể hữu ích trong các tình huống khác nhau: ví dụ: quét địa chỉ email trong tài liệu, để xác thực đầu vào của người dùng hoặc như một ràng buộc toàn vẹn trên kho lưu trữ dữ liệu.

Tuy nhiên, cần lưu ý rằng nếu bạn muốn tìm hiểu xem địa chỉ có thực sự đề cập đến hộp thư hiện có hay không, không có sự thay thế nào cho việc gửi tin nhắn đến địa chỉ. Nếu bạn chỉ muốn kiểm tra xem một địa chỉ có đúng ngữ pháp hay không thì bạn có thể sử dụng một biểu thức chính quy, nhưng lưu ý rằng""@[] là một địa chỉ email đúng ngữ pháp mà chắc chắn không đề cập đến một hộp thư hiện có.

Cú pháp của địa chỉ email đã được xác định trong các RFC khác nhau , đáng chú ý nhất là RFC 822RFC 5322 . RFC 822 nên được coi là tiêu chuẩn "nguyên bản" và RFC 5322 là tiêu chuẩn mới nhất. Cú pháp được định nghĩa trong RFC 822 là các tiêu chuẩn nhẹ nhàng nhất và tiếp theo đã hạn chế cú pháp ngày càng xa hơn, trong đó các hệ thống hoặc dịch vụ mới hơn sẽ nhận ra cú pháp lỗi thời, nhưng không bao giờ tạo ra nó.

Trong câu trả lời này, tôi sẽ lấy địa chỉ email của Cameron có nghĩa addr-speclà được định nghĩa trong RFC (nghĩa là jdoe@example.org, nhưng không "John Doe"<jdoe@example.org>, cũng không some-group:jdoe@example.org,mrx@exampel.org;).

Có một vấn đề với việc dịch các cú pháp RFC thành regexes: các cú pháp không thường xuyên! Điều này là do chúng cho phép nhận xét tùy chọn trong các địa chỉ email có thể được lồng vô hạn, trong khi lồng vô hạn không thể được mô tả bằng một biểu thức thông thường. Để quét hoặc xác thực các địa chỉ có chứa các bình luận, bạn cần một trình phân tích cú pháp hoặc các biểu thức mạnh hơn. (Lưu ý rằng các ngôn ngữ như Perl có cấu trúc để mô tả ngữ pháp miễn phí theo ngữ cảnh theo cách giống như biểu thức chính quy.) Trong câu trả lời này, tôi sẽ bỏ qua các bình luận và chỉ xem xét các biểu thức chính quy thông thường.

RFC xác định cú pháp cho thông điệp email, không phải cho địa chỉ email như vậy. Địa chỉ có thể xuất hiện trong các trường tiêu đề khác nhau và đây là nơi chúng được xác định chủ yếu. Khi chúng xuất hiện trong các trường tiêu đề, địa chỉ có thể chứa (giữa các thẻ từ vựng) khoảng trắng, nhận xét và thậm chí ngắt dòng. Về mặt ngữ nghĩa, điều này không có ý nghĩa gì. Bằng cách xóa khoảng trắng này, v.v. khỏi một địa chỉ, bạn sẽ có được một biểu diễn chính tắc tương đương về mặt ngữ nghĩa . Vì vậy, các đại diện kinh điển của first. last (comment) @ [3.5.7.9]first.last@[3.5.7.9].

Các cú pháp khác nhau nên được sử dụng cho các mục đích khác nhau. Nếu bạn muốn quét địa chỉ email trong tài liệu (có thể rất cũ), có thể nên sử dụng cú pháp như được định nghĩa trong RFC 822. Mặt khác, nếu bạn muốn xác thực đầu vào của người dùng, bạn có thể muốn sử dụng cú pháp như được định nghĩa trong RFC 5322, có lẽ chỉ chấp nhận các biểu diễn chính tắc. Bạn nên quyết định cú pháp nào áp dụng cho trường hợp cụ thể của bạn.

Tôi sử dụng các biểu thức chính quy "mở rộng" POSIX trong câu trả lời này, giả sử một bộ ký tự tương thích ASCII.

RFC 822

Tôi đến biểu hiện thường xuyên sau đây. Tôi mời mọi người dùng thử và phá vỡ nó. Nếu bạn tìm thấy bất kỳ dương tính giả hoặc phủ định sai, xin vui lòng gửi chúng trong một bình luận và tôi sẽ cố gắng sửa biểu thức càng sớm càng tốt.

([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*"))*@([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*]))*

Tôi tin rằng nó hoàn toàn phù hợp với RFC 822 bao gồm cả errata . Nó chỉ nhận ra địa chỉ email ở dạng chính tắc của họ. Đối với một regex nhận ra (gấp) khoảng trắng, xem đạo hàm bên dưới.

Đạo hàm cho thấy cách tôi đến biểu thức. Tôi liệt kê tất cả các quy tắc ngữ pháp có liên quan từ RFC chính xác khi chúng xuất hiện, theo sau là regex tương ứng. Khi một lỗi sai đã được xuất bản, tôi đưa ra một biểu thức riêng cho quy tắc ngữ pháp đã sửa (được đánh dấu là "erratum") và sử dụng phiên bản cập nhật như một biểu hiện phụ trong các biểu thức chính quy tiếp theo.

Như đã nêu trong đoạn 3.1.4. của RFC 822 khoảng trắng tuyến tính tùy chọn có thể được chèn giữa các mã thông báo từ vựng. Khi áp dụng, tôi đã mở rộng các biểu thức để phù hợp với quy tắc này và đánh dấu kết quả bằng "opt-lwsp".

CHAR        =  <any ASCII character>
            =~ .

CTL         =  <any ASCII control character and DEL>
            =~ [\x00-\x1F\x7F]

CR          =  <ASCII CR, carriage return>
            =~ \r

LF          =  <ASCII LF, linefeed>
            =~ \n

SPACE       =  <ASCII SP, space>
            =~  

HTAB        =  <ASCII HT, horizontal-tab>
            =~ \t

<">         =  <ASCII quote mark>
            =~ "

CRLF        =  CR LF
            =~ \r\n

LWSP-char   =  SPACE / HTAB
            =~ [ \t]

linear-white-space =  1*([CRLF] LWSP-char)
                   =~ ((\r\n)?[ \t])+

specials    =  "(" / ")" / "<" / ">" / "@" /  "," / ";" / ":" / "\" / <"> /  "." / "[" / "]"
            =~ [][()<>@,;:\\".]

quoted-pair =  "\" CHAR
            =~ \\.

qtext       =  <any CHAR excepting <">, "\" & CR, and including linear-white-space>
            =~ [^"\\\r]|((\r\n)?[ \t])+

dtext       =  <any CHAR excluding "[", "]", "\" & CR, & including linear-white-space>
            =~ [^][\\\r]|((\r\n)?[ \t])+

quoted-string  =  <"> *(qtext|quoted-pair) <">
               =~ "([^"\\\r]|((\r\n)?[ \t])|\\.)*"
(erratum)      =~ "(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"

domain-literal =  "[" *(dtext|quoted-pair) "]"
               =~ \[([^][\\\r]|((\r\n)?[ \t])|\\.)*]
(erratum)      =~ \[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]

atom        =  1*<any CHAR except specials, SPACE and CTLs>
            =~ [^][()<>@,;:\\". \x00-\x1F\x7F]+

word        =  atom / quoted-string
            =~ [^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"

domain-ref  =  atom

sub-domain  =  domain-ref / domain-literal
            =~ [^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]

local-part  =  word *("." word)
            =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"))*
(opt-lwsp)  =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")(((\r\n)?[ \t])*\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"))*

domain      =  sub-domain *("." sub-domain)
            =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*
(opt-lwsp)  =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(((\r\n)?[ \t])*\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*

addr-spec   =  local-part "@" domain
            =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"))*@([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*
(opt-lwsp)  =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")((\r\n)?[ \t])*(\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")((\r\n)?[ \t])*)*@((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(((\r\n)?[ \t])*\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*
(canonical) =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*"))*@([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*]))*

RFC 5322

Tôi đến biểu hiện thường xuyên sau đây. Tôi mời mọi người dùng thử và phá vỡ nó. Nếu bạn tìm thấy bất kỳ dương tính giả hoặc phủ định sai, xin vui lòng gửi chúng trong một bình luận và tôi sẽ cố gắng sửa biểu thức càng sớm càng tốt.

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*])

Tôi tin rằng nó hoàn toàn phù hợp với RFC 5322 bao gồm cả errata . Nó chỉ nhận ra địa chỉ email ở dạng chính tắc của họ. Đối với một regex nhận ra (gấp) khoảng trắng, xem đạo hàm bên dưới.

Đạo hàm cho thấy cách tôi đến biểu thức. Tôi liệt kê tất cả các quy tắc ngữ pháp có liên quan từ RFC chính xác khi chúng xuất hiện, theo sau là regex tương ứng. Đối với các quy tắc bao gồm khoảng trắng (gấp) không liên quan về mặt ngữ nghĩa, tôi đưa ra một biểu thức chính được đánh dấu "(chuẩn hóa)" không chấp nhận khoảng trắng này.

Tôi đã bỏ qua tất cả các quy tắc "quan sát" từ RFC. Điều này có nghĩa là các biểu thức chỉ phù hợp với các địa chỉ email tuân thủ nghiêm ngặt RFC 5322. Nếu bạn phải khớp các địa chỉ "cũ" (như ngữ pháp lỏng hơn bao gồm các quy tắc "obs-"), bạn có thể sử dụng một trong các biểu thức RFC 822 từ đoạn trước.

VCHAR           =   %x21-7E
                =~  [!-~]

ALPHA           =   %x41-5A / %x61-7A
                =~  [A-Za-z]

DIGIT           =   %x30-39
                =~  [0-9]

HTAB            =   %x09
                =~  \t

CR              =   %x0D
                =~  \r

LF              =   %x0A
                =~  \n

SP              =   %x20
                =~  

DQUOTE          =   %x22
                =~  "

CRLF            =   CR LF
                =~  \r\n

WSP             =   SP / HTAB
                =~  [\t ]

quoted-pair     =   "\" (VCHAR / WSP)
                =~  \\[\t -~]

FWS             =   ([*WSP CRLF] 1*WSP)
                =~  ([\t ]*\r\n)?[\t ]+

ctext           =   %d33-39 / %d42-91 / %d93-126
                =~  []!-'*-[^-~]

("comment" is left out in the regex)
ccontent        =   ctext / quoted-pair / comment
                =~  []!-'*-[^-~]|(\\[\t -~])

(not regular)
comment         =   "(" *([FWS] ccontent) [FWS] ")"

(is equivalent to FWS when leaving out comments)
CFWS            =   (1*([FWS] comment) [FWS]) / FWS
                =~  ([\t ]*\r\n)?[\t ]+

atext           =   ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
                =~  [-!#-'*+/-9=?A-Z^-~]

dot-atom-text   =   1*atext *("." 1*atext)
                =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*

dot-atom        =   [CFWS] dot-atom-text [CFWS]
                =~  (([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*

qtext           =   %d33 / %d35-91 / %d93-126
                =~  []!#-[^-~]

qcontent        =   qtext / quoted-pair
                =~  []!#-[^-~]|(\\[\t -~])

(erratum)
quoted-string   =   [CFWS] DQUOTE ((1*([FWS] qcontent) [FWS]) / FWS) DQUOTE [CFWS]
                =~  (([\t ]*\r\n)?[\t ]+)?"(((([\t ]*\r\n)?[\t ]+)?([]!#-[^-~]|(\\[\t -~])))+(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?)"(([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  "([]!#-[^-~ \t]|(\\[\t -~]))+"

dtext           =   %d33-90 / %d94-126
                =~  [!-Z^-~]

domain-literal  =   [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
                =~  (([\t ]*\r\n)?[\t ]+)?\[((([\t ]*\r\n)?[\t ]+)?[!-Z^-~])*(([\t ]*\r\n)?[\t ]+)?](([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  \[[\t -Z^-~]*]

local-part      =   dot-atom / quoted-string
                =~  (([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?"(((([\t ]*\r\n)?[\t ]+)?([]!#-[^-~]|(\\[\t -~])))+(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?)"(([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+"

domain          =   dot-atom / domain-literal
                =~  (([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?\[((([\t ]*\r\n)?[\t ]+)?[!-Z^-~])*(([\t ]*\r\n)?[\t ]+)?](([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*]

addr-spec       =   local-part "@" domain
                =~  ((([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?"(((([\t ]*\r\n)?[\t ]+)?([]!#-[^-~]|(\\[\t -~])))+(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?)"(([\t ]*\r\n)?[\t ]+)?)@((([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?\[((([\t ]*\r\n)?[\t ]+)?[!-Z^-~])*(([\t ]*\r\n)?[\t ]+)?](([\t ]*\r\n)?[\t ]+)?)
(normalized)    =~  ([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*])

Lưu ý rằng một số nguồn (đáng chú ý là w3c ) cho rằng RFC 5322 quá nghiêm ngặt đối với phần cục bộ (tức là phần trước @ -sign). Điều này là do "..", "a..b" và "a." không phải là các nguyên tử chấm hợp lệ, trong khi chúng có thể được sử dụng làm tên hộp thư. RFC, tuy nhiên, không cho phép cho các bộ phận địa phương như thế này, ngoại trừ việc họ đã được trích dẫn. Vì vậy, thay vì a..b@example.netbạn nên viết "a..b"@example.net, đó là tương đương về mặt ngữ nghĩa.

Hạn chế hơn nữa

SMTP (như được định nghĩa trong RFC 5321 ) tiếp tục hạn chế tập hợp các địa chỉ email hợp lệ (hoặc thực tế: tên hộp thư). Có vẻ hợp lý để áp đặt ngữ pháp chặt chẽ hơn này, để địa chỉ email phù hợp thực sự có thể được sử dụng để gửi email.

Về cơ bản, RFC 5321 chỉ để lại phần "cục bộ" (tức là phần trước phần @ -ign), nhưng chặt chẽ hơn về phần miền (tức là phần sau phần @ -ign). Nó chỉ cho phép lưu trữ tên thay cho các nguyên tử dấu chấm và địa chỉ bằng chữ thay cho tên miền.

Ngữ pháp được trình bày trong RFC 5321 quá nhẹ nhàng khi nói đến cả tên máy chủ và địa chỉ IP. Tôi đã tự do "sửa chữa" các quy tắc trong câu hỏi, sử dụng dự thảo nàyRFC 1034 làm hướng dẫn. Đây là regex kết quả.

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*|\[((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+)])

Lưu ý rằng tùy thuộc vào trường hợp sử dụng, bạn có thể không muốn cho phép "Địa chỉ chung theo nghĩa đen" trong biểu thức chính thức của mình. Cũng lưu ý rằng tôi đã sử dụng giao diện phủ định (?!IPv6:)trong regex cuối cùng để ngăn phần "Địa chỉ chung theo nghĩa đen" khớp với các địa chỉ IPv6 không đúng định dạng. Một số bộ xử lý regex không hỗ trợ giao diện tiêu cực. Xóa chuỗi con |(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+khỏi regex nếu bạn muốn lấy toàn bộ phần "Địa chỉ chung" theo nghĩa đen.

Đây là đạo hàm:

Let-dig         =   ALPHA / DIGIT
                =~  [0-9A-Za-z]

Ldh-str         =   *( ALPHA / DIGIT / "-" ) Let-dig
                =~  [0-9A-Za-z-]*[0-9A-Za-z]

(regex is updated to make sure sub-domains are max. 63 charactes long - RFC 1034 section 3.5)
sub-domain      =   Let-dig [Ldh-str]
                =~  [0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?

Domain          =   sub-domain *("." sub-domain)
                =~  [0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*

Snum            =   1*3DIGIT
                =~  [0-9]{1,3}

(suggested replacement for "Snum")
ip4-octet       =   DIGIT / %x31-39 DIGIT / "1" 2DIGIT / "2" %x30-34 DIGIT / "25" %x30-35
                =~  25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]

IPv4-address-literal    =   Snum 3("."  Snum)
                        =~  [0-9]{1,3}(\.[0-9]{1,3}){3}

(suggested replacement for "IPv4-address-literal")
ip4-address     =   ip4-octet 3("." ip4-octet)
                =~  (25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}

(suggested replacement for "IPv6-hex")
ip6-h16         =   "0" / ( (%x49-57 / %x65-70 /%x97-102) 0*3(%x48-57 / %x65-70 /%x97-102) )
                =~  0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}

(not from RFC)
ls32            =   ip6-h16 ":" ip6-h16 / ip4-address
                =~  (0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}

(suggested replacement of "IPv6-addr")
ip6-address     =                                      6(ip6-h16 ":") ls32
                    /                             "::" 5(ip6-h16 ":") ls32
                    / [                 ip6-h16 ] "::" 4(ip6-h16 ":") ls32
                    / [ *1(ip6-h16 ":") ip6-h16 ] "::" 3(ip6-h16 ":") ls32
                    / [ *2(ip6-h16 ":") ip6-h16 ] "::" 2(ip6-h16 ":") ls32
                    / [ *3(ip6-h16 ":") ip6-h16 ] "::"   ip6-h16 ":"  ls32
                    / [ *4(ip6-h16 ":") ip6-h16 ] "::"                ls32
                    / [ *5(ip6-h16 ":") ip6-h16 ] "::"   ip6-h16
                    / [ *6(ip6-h16 ":") ip6-h16 ] "::"
                =~  (((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::

IPv6-address-literal    =   "IPv6:" ip6-address
                        =~  IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)

Standardized-tag        =   Ldh-str
                        =~  [0-9A-Za-z-]*[0-9A-Za-z]

dcontent        =   %d33-90 / %d94-126
                =~  [!-Z^-~]

General-address-literal =   Standardized-tag ":" 1*dcontent
                        =~  [0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+

address-literal =   "[" ( IPv4-address-literal / IPv6-address-literal / General-address-literal ) "]"
                =~  \[((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+)]

Mailbox         =   Local-part "@" ( Domain / address-literal )
                =~  ([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*|\[((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+)])

Xác thực người dùng

Trường hợp sử dụng phổ biến là xác thực nhập liệu của người dùng, ví dụ trên biểu mẫu html. Trong trường hợp đó, thường là hợp lý để loại trừ địa chỉ bằng chữ và yêu cầu ít nhất hai nhãn trong tên máy chủ. Lấy regex RFC 5321 được cải tiến từ phần trước làm cơ sở, biểu thức kết quả sẽ là:

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)+

Tôi không khuyên bạn nên hạn chế phần cục bộ hơn nữa, ví dụ: bằng cách loại trừ các chuỗi được trích dẫn, vì chúng tôi không biết loại tên hộp thư nào mà một số máy chủ cho phép (thích "a..b"@example.nethoặc thậm chí "a b"@example.net).

Tôi cũng không khuyên bạn nên xác nhận rõ ràng đối với danh sách các tên miền cấp cao nhất hoặc thậm chí áp đặt các ràng buộc về độ dài (hãy nhớ cách ".museum" bị vô hiệu [a-z]{2,4}), nhưng nếu bạn phải:

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?\.)*(net|org|com|info|Vân vân...)

Đảm bảo cập nhật biểu thức chính quy của bạn nếu bạn quyết định đi theo con đường xác thực tên miền cấp cao rõ ràng.

Cân nhắc thêm

Khi chỉ chấp nhận tên máy chủ trong phần tên miền (sau @ -sign), các biểu thức trên chỉ chấp nhận các nhãn có tối đa 63 ký tự, nếu cần. Tuy nhiên, họ không thực thi thực tế là toàn bộ tên máy chủ phải dài tối đa 253 ký tự (bao gồm cả dấu chấm). Mặc dù hạn chế này được nói một cách nghiêm túc vẫn đều đặn, nhưng việc đưa ra một biểu thức kết hợp quy tắc này là không khả thi.

Một cân nhắc khác, đặc biệt là khi sử dụng các biểu thức chính để xác thực đầu vào, là phản hồi cho người dùng. Nếu người dùng nhập địa chỉ không chính xác, sẽ tốt hơn nếu cung cấp thêm một chút thông tin phản hồi so với "địa chỉ cú pháp sai" đơn giản. Với regexes "vanilla", điều này là không thể.

Hai cân nhắc này có thể được giải quyết bằng cách phân tích địa chỉ. Trong một số trường hợp, ràng buộc độ dài thêm đối với tên máy chủ có thể được giải quyết bằng cách sử dụng biểu thức chính bổ sung để kiểm tra nó và khớp địa chỉ với cả hai biểu thức.

Không có biểu thức nào trong câu trả lời này được tối ưu hóa cho hiệu suất. Nếu hiệu suất là một vấn đề, bạn nên xem liệu (và làm thế nào) biểu thức bạn chọn có thể được tối ưu hóa hay không.


3
RFC 6532 cập nhật 5322 để cho phép và bao gồm UTF-8 đầy đủ, sạch sẽ. Chi tiết bổ sung tại đây .

Theo wikipedia dường như phần cục bộ, khi được chấm, có giới hạn 64 ký tự cho mỗi phần, và RFC 5322 đề cập đến phần cục bộ được chấm để giải thích với các hạn chế của các miền. Ví dụ arbitrary-long-email-address-should-be-invalid-arbitrary-long-email-address-should-be-invalid.and-the-second-group-also-should-not-be-so-long-and-the-second-group-also-should-not-be-so-long@example.comkhông nên xác nhận. Tôi đề nghị thay đổi các dấu "+" trong nhóm đầu tiên (tên trước dấu chấm tùy chọn) và trong nhóm thứ hai (tên sau các dấu chấm sau) thành{1,64}
Xavi Montero

Vì các bình luận bị giới hạn về kích thước, đây là kết quả mà tôi dự định sử dụng, đây là câu trả lời ở đầu câu trả lời này, cộng với việc giới hạn kích thước ở phần cục bộ, cộng với thêm dấu gạch chéo ngược trước dấu "/" biểu tượng theo yêu cầu của PHP và cả trong regex101.com: Trong PHP tôi sử dụng:$emailRegex = '/^([-!#-\'*+\/-9=?A-Z^-~]{1,64}(\.[-!#-\'*+\/-9=?A-Z^-~]{1,64})*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)+$/';
Xavi Montero

THẬN TRỌNG: Vì một số lý do, StackOverflow thêm các ký tự ẩn khi sao chép từ đánh dấu kết xuất. Sao chép nó vào regex101.com và bạn sẽ thấy các chấm đen ở đó. Bạn phải loại bỏ chúng và sửa chuỗi ... Có thể nếu được tích hợp trong câu trả lời, chúng có thể được điều chỉnh chính xác. Xin lỗi vì sự bất tiện. Tôi không muốn thêm một câu trả lời mới vì đây là câu trả lời thích hợp. Ngoài ra tôi không muốn trực tiếp chỉnh sửa trừ khi cộng đồng nghĩ rằng điều này nên được tích hợp vào nó.
Xavi Montero

@XaviMontero Thaks vì đã đóng góp Xavi! Bạn có tham chiếu đến RFC nêu giới hạn 64 ký tự trên nhãn phần cục bộ không? Nếu vậy, tôi sẽ vui lòng điều chỉnh câu trả lời.
Rinke

73

Có rất nhiều ví dụ về điều này trên mạng (và tôi nghĩ thậm chí một ví dụ xác nhận đầy đủ RFC - nhưng nó dài hàng chục / hàng trăm dòng nếu bộ nhớ phục vụ). Mọi người có xu hướng được mang đi xác nhận loại điều này. Tại sao không kiểm tra nó có @ và ít nhất một. và đáp ứng một số chiều dài tối thiểu đơn giản. Việc nhập một email giả mạo và vẫn phù hợp với bất kỳ regex hợp lệ nào. Tôi đoán rằng dương tính giả tốt hơn âm tính giả.


1
Có, nhưng RFC? :) Trình xác thực [RFC ‐ 5322 này] ( stackoverflow.com/questions/201323/NH ) chỉ dài khoảng bốn mươi dòng.
tchrist

14
A. không được yêu cầu. Một TLD có thể có địa chỉ email hoặc có thể có địa chỉ IPv6
Sijmen Mulder

1
RFC không phải là kết thúc của câu chuyện: ICANN không cho phép các tên miền 'không có dấu vết' nữa: icann.org/news/announcement-2013-08-30-en
Synchro

64

Trong khi quyết định những nhân vật nào được cho phép, xin vui lòng nhớ những người bạn bị bỏ mặc và gạch nối của bạn. Tôi không kiểm soát được việc công ty tôi tạo địa chỉ email bằng tên của tôi từ hệ thống nhân sự. Điều đó bao gồm dấu nháy đơn trong tên cuối cùng của tôi. Tôi không thể cho bạn biết tôi đã bị chặn tương tác với một trang web bao nhiêu lần bởi thực tế là địa chỉ email của tôi là "không hợp lệ".


4
Đây là một vấn đề siêu phổ biến trong các chương trình đưa ra các giả định không chính đáng về những gì được và không được phép trong tên của một người. Người ta không nên đưa ra các giả định như vậy, chỉ cần chấp nhận bất kỳ nhân vật nào mà RFC (s) có liên quan nói rằng phải có.
tchrist

4
Đúng. Tôi đặc biệt tức giận chống lại các lập trình viên từ chối chữ in hoa trong địa chỉ email! Ngớ ngẩn và / hoặc lười biếng.
PhiLho

63

Regex này là từ Email của Perl :: Thư viện hợp lệ . Tôi tin rằng nó là chính xác nhất, nó phù hợp với tất cả 822. Và, nó dựa trên biểu thức thông thường trong cuốn sách O'Reilly:

Biểu thức chính quy được xây dựng bằng ví dụ của Jeffrey Friedl trong Làm chủ biểu thức chính quy ( http://www.ora.com/catalog/regapi/ ).

$RFC822PAT = <<'EOF';
[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\
xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xf
f\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\x
ff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015
"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\
xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80
-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*
)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\
\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\
x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x8
0-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n
\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x
80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^
\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040
\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([
^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\
\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\
x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-
\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()
]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\
x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\04
0\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\
n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\
015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?!
[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\
]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\
x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\01
5()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".
\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]
)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^
()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\0
15()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][
^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\
n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\
x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?
:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-
\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*
(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015
()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()
]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\0
40)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\
[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\
xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*
)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80
-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x
80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t
]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\
\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])
*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x
80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80
-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015(
)]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\
\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t
]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\0
15()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015
()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(
\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|
\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80
-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()
]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x
80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^
\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040
\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".
\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff
])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\
\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x
80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015
()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\
\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^
(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-
\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\
n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|
\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))
[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff
\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\x
ff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(
?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\
000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\
xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\x
ff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)
*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\x
ff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-
\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)
*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\
]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\]
)[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-
\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\x
ff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(
?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80
-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<
>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x8
0-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:
\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]
*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)
*\)[\040\t]*)*)*>)
EOF

14
O_O bạn cũng cần phải là một bậc thầy regex để hiểu những gì nó đang làm
Chris McGrath

45

Khi bạn viết bằng PHP, tôi khuyên bạn nên sử dụng xác thực tích hợp PHP cho email.

filter_var($value, FILTER_VALIDATE_EMAIL)

Nếu bạn đang chạy phiên bản php thấp hơn 5.3.6, vui lòng lưu ý vấn đề này: https://bugs.php.net/orms.php?id=53091

Nếu bạn muốn biết thêm thông tin về cách xác thực buid-in này hoạt động, hãy xem tại đây: Bộ lọc của PHP_var FILTER_VALIDATE_EMAIL có thực sự hoạt động không?


được một phiếu bầu, chính xác những gì tôi sẽ nói. Không xử lý IDN nhưng chuyển đổi sang mã trừng phạt trước sẽ giải quyết điều này. PHP> = 5.3 có idn_to_ascii () cho việc này. Một trong những cách tốt nhất và dễ nhất để xác nhận email.
Taylor

43

Cal Henderson (Flickr) đã viết một bài báo có tên Phân tích địa chỉ email trong PHP và chỉ ra cách thực hiện phân tích địa chỉ email tuân thủ RFC (2) 822 phù hợp. Bạn cũng có thể lấy mã nguồn bằng php , python và ruby ​​được cấp phép cc .


nó nói với tôi rằng điều đó a@blà hợp lệ
dsdsdsdsd

1
@dsdsdsdsd Vì a@bhợp lệ ... trong trường hợp bnày là tên miền cấp cao nhất.
rink.attguard.6

42

Tôi không bao giờ bận tâm đến việc tạo ra với biểu thức chính quy của riêng mình, bởi vì rất có thể là người khác đã đưa ra một phiên bản tốt hơn. Tôi luôn luôn sử dụng regexlib để tìm một cái theo ý thích của mình.


1
Điều này đã được gắn cờ về độ dài và nội dung, nhưng nó vẫn đóng góp tốt với 41 phiếu bầu và không nên bị xóa.
Sẽ

37

Không có cái nào thực sự có thể sử dụng được.
Tôi thảo luận về một số vấn đề trong câu trả lời của tôi với Có thư viện php để xác thực địa chỉ email không? , nó cũng được thảo luận trong việc nhận diện địa chỉ email khó?

Nói tóm lại, đừng mong đợi một regex duy nhất, có thể sử dụng để thực hiện một công việc phù hợp. Và regex tốt nhất sẽ xác thực cú pháp, không phải tính hợp lệ của e-mail (jhohn@example.com là chính xác nhưng nó có thể sẽ bị trả lại ...).


Sửa lỗi cho tôi nếu tôi sai, nhưng tôi tin rằng PHP sử dụng các mẫu PCRE. Nếu vậy, bạn sẽ có thể tạo ra một cái gì đó tương tự như mẫu RFC 5322 của Abigail .
tchrist

@tchrist: không chắc PCRE có bắt kịp cú pháp này không (mà tôi phát hiện ra). Nếu vậy, không chắc PCRE của PHP có bắt kịp phiên bản PCRE này không ... Chà, nếu tôi hiểu chính xác cú pháp này, bạn cũng có thể sử dụng trình phân tích cú pháp PEG, rõ ràng và đầy đủ hơn so với regex.
PhiLho

PCRE đã bắt kịp nó, nhưng có lẽ PHP đã không bắt kịp PCRE. ☹
tchrist

36

Một biểu thức chính quy đơn giản mà ít nhất sẽ không từ chối bất kỳ địa chỉ email hợp lệ nào sẽ được kiểm tra một cái gì đó, theo sau là dấu @ và sau đó là một khoảng thời gian và ít nhất là 2 lần. Nó sẽ không từ chối bất cứ điều gì, nhưng sau khi xem xét thông số kỹ thuật tôi không thể tìm thấy bất kỳ email nào hợp lệ và bị từ chối.

email = ~ /.+@[^@]+\.[^@]{2,}$/


3
Đây là những gì tôi đang tìm kiếm. Không hạn chế lắm, nhưng đảm bảo chỉ có 1 @ (vì chúng tôi đang phân tích danh sách và muốn đảm bảo không có dấu phẩy bị thiếu). FYI, bạn có thể có @ ở bên trái nếu nó được trích dẫn: Valid_email_addresses , nhưng nó rất đẹp.
Josh

2
Sau khi sử dụng, nhận ra nó không hoạt động chính xác. /^[^@]+@[^@]+\.[^@]{2}[^@]*$/ thực sự kiểm tra dấu 1 @. Regex của bạn sẽ cho phép nhiều thông qua vì. * Ở cuối.
Josh

1
Đúng. Tôi không cố gắng từ chối tất cả không hợp lệ, chỉ cần từ chối một địa chỉ email hợp lệ.
spig

1
Sẽ tốt hơn nhiều khi sử dụng điều này: /^[^@]+@[^@]+\.[^@]{2,4}$/đảm bảo rằng nó kết thúc với 2 đến 4 ký tự không @. Như @Josh đã chỉ ra, bây giờ nó cho phép thêm @ cuối cùng. Nhưng bạn cũng có thể thay đổi điều đó thành: /^[^@]+@[^@]+\.[^a-z-A-Z]{2,4}$/vì tất cả các tên miền cấp cao nhất là ký tự aZ. bạn cũng có thể thay thế 4bằng 5hoặc nhiều hơn cho phép các tên miền cấp cao nhất sẽ dài hơn trong tương lai.
FLY

@FLY, ka @ foo. trả về đúng Nó được cho là, theo các tiêu chuẩn?
SexyBeast

29

Bạn có thể sử dụng cái được sử dụng bởi plugin Xác thực jQuery:

/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i

điều này dường như đang làm một công việc tốt. Nó cho phép: a-b'c_d.e@f-g.hnhưng đã có thể bắt được các biến thể không phù hợp, chẳng hạn như a-b'c_d.@f-g.ha-b'c_d.e@f-.h
dsdsdsdsd

25

Để đánh giá toàn diện nhất về biểu thức chính quy tốt nhất để xác thực địa chỉ email, vui lòng xem liên kết này; " So sánh địa chỉ email xác thực biểu thức chính quy "

Đây là biểu thức hàng đầu hiện tại cho mục đích tham khảo:

/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i

Spoon16: Liên kết đó không thực sự chính xác. Tuyên bố của nó rằng không thể có mẫu hoàn hảo để xác nhận địa chỉ email là lỗi rõ ràng. Bạn có thể , nhưng bạn phải chắc chắn rằng bạn theo RFC ngay đến thư. Và bạn cũng phải chọn đúng RFC.
tchrist

"Tốt nhất" ngay bây giờ không hoạt động với java regex - ngay cả sau khi thoát và chuyển đổi chuỗi đúng cách.
Eric Chen

23

Không đề cập đến việc các tên miền không phải là tiếng Latin (Trung Quốc, Ả Rập, Hy Lạp, Do Thái, Cyrillic và vv) sẽ được cho phép trong tương lai gần . Mọi người phải thay đổi regex email được sử dụng, bởi vì những ký tự đó chắc chắn không được bao phủ bởi [a-z]/icũng không \w. Tất cả họ sẽ thất bại.

Rốt cuộc, cách tốt nhất để xác thực địa chỉ email vẫn là thực sự gửi email đến địa chỉ được đề cập để xác thực địa chỉ. Nếu địa chỉ email là một phần của xác thực người dùng (đăng ký / đăng nhập / vv), thì bạn hoàn toàn có thể kết hợp nó với hệ thống kích hoạt người dùng. Tức là gửi email có liên kết với khóa kích hoạt duy nhất đến địa chỉ email được chỉ định và chỉ cho phép đăng nhập khi người dùng đã kích hoạt tài khoản mới tạo bằng liên kết trong email.

Nếu mục đích của regex chỉ là thông báo nhanh chóng cho người dùng trong giao diện người dùng rằng địa chỉ email được chỉ định không giống với định dạng đúng, tốt nhất vẫn là kiểm tra xem về cơ bản có khớp với regex sau không:

^([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)$

Đơn giản như thế. Tại sao bạn lại quan tâm đến các ký tự được sử dụng trong tên và miền? Khách hàng có trách nhiệm nhập địa chỉ email hợp lệ, không phải của máy chủ. Ngay cả khi khách hàng bước vào một cú pháp địa chỉ email hợp lệ như aa@bb.cc, điều này không đảm bảo rằng đó là một địa chỉ email hợp pháp. Không ai regex có thể bao gồm điều đó.


4
Tôi đồng ý gửi tin nhắn xác thực thường là cách tốt nhất cho loại nội dung này, về mặt cú pháp chính xác và hợp lệ không giống nhau. Tôi cảm thấy thất vọng khi tôi được thực hiện để gõ địa chỉ email của mình hai lần cho "Xác nhận" như thể tôi không thể nhìn vào những gì tôi đã nhập. Tôi chỉ sao chép cái đầu tiên sang cái thứ hai dù sao, nó dường như ngày càng được sử dụng nhiều hơn.
PeteT

đồng ý! nhưng regex này tôi không nghĩ là hợp lệ vì nó cho phép spacessau @.ví dụ. test@test.ca com netđược xem xét một email hợp lệ bằng cách sử dụng regex ở trên vì nó sẽ trả về không hợp lệ.
CB4

20

Thông số HTML5 gợi ý một biểu thức chính quy đơn giản để xác thực địa chỉ email:

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/

Điều này cố ý không tuân thủ RFC 5322 .

Lưu ý: Yêu cầu này là một vi phạm cố ý của RFC 5322 , trong đó xác định một cú pháp cho các địa chỉ e-mail mà là cùng một lúc quá khắt khe (trước khi @ký tự), quá mơ hồ (sau khi @nhân vật), và quá lỏng lẻo (cho phép bình luận, khoảng trắng nhân vật, và trích dẫn các chuỗi trong cách cư xử xa lạ với hầu hết người dùng) sẽ được sử dụng thực tế ở đây.

Tổng chiều dài cũng có thể được giới hạn ở mức 254 ký tự, trên mỗi RFC 3696 errata 1690 .


Câu trả lời tốt nhất! Đây là một liên kết đến đề xuất w3: w3.org/TR/html5/forms.html#valid-e-mail-address Regex này được nhiều trình duyệt chấp nhận.
Ryan Taylor

3
Đây không phải là câu trả lời tốt nhất! Mẫu này khớp với địa chỉ hoàn toàn không hợp lệ này : invalid@emailaddress. Tôi sẽ khuyến khích thận trọng và thử nghiệm nhiều trước khi bạn sử dụng nó!
Sheridan

@Sheridan, nếu bạn nghĩ rằng có vấn đề với thông số HTML5, bạn có thể nêu ra một vấn đề ở đây: github.com/w3c/html/issues
Luna

Điều này không thêm nhiều hơn stackoverflow.com/a/8829363 và IMHO sẽ tốt hơn khi chỉnh sửa hoặc nhận xét về điều đó.

example @ localhost là hợp lệ, nhưng đối với một ứng dụng trong thế giới thực, bạn có thể muốn thực thi một phần mở rộng tên miền, tất cả những gì bạn cần làm là thay đổi cuối cùng * thành + để đạt được điều này (thay đổi một phần của mẫu từ 0+ thành 1+ )
Mitch Satchwell

15

Đối với một minh chứng sống động, con quái vật sau đây khá tốt nhưng vẫn không nhận ra chính xác tất cả các địa chỉ email hợp lệ về mặt cú pháp: nó nhận ra các bình luận lồng nhau sâu đến bốn cấp.

Đây là một công việc cho một trình phân tích cú pháp, nhưng ngay cả khi một địa chỉ có giá trị cú pháp, nó vẫn có thể không được cung cấp. Đôi khi bạn phải dùng đến phương pháp Hillbilly của "Hey, y'all, xem ee-us!"

// derivative of work with the following copyright and license:
// Copyright (c) 2004 Casey West.  All rights reserved.
// This module is free software; you can redistribute it and/or
// modify it under the same terms as Perl itself.

// see http://search.cpan.org/~cwest/Email-Address-1.80/

private static string gibberish = @"
(?-xism:(?:(?-xism:(?-xism:(?-xism:(?-xism:(?-xism:(?-xism:\
s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^
\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))
|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+
|\s+)*[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+(?-xism:(?-xism:\
s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^
\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))
|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+
|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(
?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?
:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x
0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*<DQ>(?-xism:(?-xism:[
^\\<DQ>])|(?-xism:\\(?-xism:[^\x0A\x0D])))+<DQ>(?-xism:(?-xi
sm:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xis
m:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\
]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\
s*)+|\s+)*))+)?(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?
-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:
\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[
^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*<(?-xism:(?-xi
sm:(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^(
)\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(
?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))
|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()<
>\[\]:;@\,.<DQ>\s]+(?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]
+)*)(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))
|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:
(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s
*\)\s*))+)*\s*\)\s*)+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?
:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x
0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xi
sm:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*
<DQ>(?-xism:(?-xism:[^\\<DQ>])|(?-xism:\\(?-xism:[^\x0A\x0D]
)))+<DQ>(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\
]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-x
ism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+
)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*))\@(?-xism:(?-xism:(?-xism:(
?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?
-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^
()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s
*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+(
?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+)*)(?-xism:(?-xism:
\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[
^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+)
)|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)
+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:
(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((
?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\
x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*\[(?:\s*(?-xism:(?-x
ism:[^\[\]\\])|(?-xism:\\(?-xism:[^\x0A\x0D])))+)*\s*\](?-xi
sm:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:
\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(
?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+
)*\s*\)\s*)+|\s+)*)))>(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-
xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\
s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^
\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*))|(?-xism:(?-x
ism:(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^
()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*
(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D])
)|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()
<>\[\]:;@\,.<DQ>\s]+(?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s
]+)*)(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+)
)|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism
:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\
s*\)\s*))+)*\s*\)\s*)+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((
?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\
x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-x
ism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)
*<DQ>(?-xism:(?-xism:[^\\<DQ>])|(?-xism:\\(?-xism:[^\x0A\x0D
])))+<DQ>(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\
\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-
xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)
+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*))\@(?-xism:(?-xism:(?-xism:
(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(
?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[
^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\
s*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+
(?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+)*)(?-xism:(?-xism
:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:
[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+
))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*
)+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism
:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\(
(?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A
\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*\[(?:\s*(?-xism:(?-
xism:[^\[\]\\])|(?-xism:\\(?-xism:[^\x0A\x0D])))+)*\s*\](?-x
ism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism
:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:
(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))
+)*\s*\)\s*)+|\s+)*))))(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?
>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:
\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0
D]))|)+)*\s*\)\s*))+)*\s*\)\s*)*)"
  .Replace("<DQ>", "\"")
  .Replace("\t", "")
  .Replace(" ", "")
  .Replace("\r", "")
  .Replace("\n", "");

private static Regex mailbox =
  new Regex(gibberish, RegexOptions.ExplicitCapture); 

12

Theo regex email hợp lệ tiêu chuẩn chính thức RFC 2822

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

nếu bạn muốn sử dụng nó trong Java thì thực sự rất dễ dàng

import java.util.regex.*;

class regexSample 
{
   public static void main(String args[]) 
   {
      //Input the string for validation
      String email = "xyz@hotmail.com";

      //Set the email pattern string
      Pattern p = Pattern.compile(" (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"
              +"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")"
                     + "@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\]");

      //Match the given string with the pattern
      Matcher m = p.matcher(email);

      //check whether match is found 
      boolean matchFound = m.matches();

      if (matchFound)
        System.out.println("Valid Email Id.");
      else
        System.out.println("Invalid Email Id.");
   }
}

1
Regex của bạn không bao gồm chữ cái viết hoa đầu tiên, ví dụ Leonardo.davinci @ gmail có thể gây khó chịu cho một số người dùng. Sử dụng cái này thay thế:(?:[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
Kebab Krabby

@KebabKrabby Cảm ơn, vui lòng chỉnh sửa câu trả lời, tôi sẽ chấp nhận thay đổi.
AZ_

Nếu tôi thêm thay đổi đó vào câu trả lời của bạn thì nó sẽ không còn là RFC 2822 nữa nên tôi không biết điều đó có đúng không.
Kebab Krabby

11

Đây là PHP tôi sử dụng. Tôi đã chọn giải pháp này theo tinh thần "dương tính giả tốt hơn âm tính giả" như tuyên bố của một người bình luận khác ở đây VÀ liên quan đến việc giữ thời gian phản hồi của bạn và tải xuống máy chủ ... thực sự không cần phải lãng phí tài nguyên máy chủ một biểu thức chính quy khi điều này sẽ loại bỏ lỗi người dùng đơn giản nhất. Bạn luôn có thể theo dõi điều này bằng cách gửi email kiểm tra nếu bạn muốn.

function validateEmail($email) {
  return (bool) stripos($email,'@');
}

1
a) "Tài nguyên máy chủ lãng phí" là vô cùng lớn, nhưng nếu bạn rất có khuynh hướng, bạn có thể thực hiện phía máy khách với JS b) Bạn cần gì để gửi thư đăng ký và người dùng nhập vào tôi @ quênthedotcom? "Giải pháp" của bạn thất bại và bạn mất một người dùng.
johnjohn

a) Dựa vào xác thực JS sẽ thất bại khi JavaScript bị vô hiệu hóa có vẻ không phải là ý tưởng hay nhất (chỉ btw)
auco

11

Tiêu chuẩn RFC 5322:

Cho phép phần cục bộ của phần tử cục bộ, phần địa phương, phần trích dẫn, phần lỗi thời (tên miền hỗn hợp và phần trích dẫn) phần tên miền, tên miền tên miền, (địa chỉ IPv6 được ánh xạ IPv4, tên miền IPv6) và (lồng nhau) CFWS.

'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD'

Tiêu chuẩn RFC 5321:

Cho phép tên miền cục bộ phần tử, phần địa phương, chuỗi tên miền, và tên miền (IPv4, IPv6 và địa chỉ IPv6 được ánh xạ IPv4) tên miền.

'/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!"?(?>\\\[ -~]|[^"]){65,}"?@)(?>([!#-\'*+\/-9=?^-~-]+)(?>\.(?1))*|"(?>[ !#-\[\]-~]|\\\[ -~])*")@(?!.*[^.]{64,})(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?2)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?3)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?3)(?>:(?3)){0,6})?::(?4)?))|(?>(?>IPv6:(?>(?3)(?>:(?3)){5}:|(?!(?:.*[a-f0-9]:){6,})(?5)?::(?>((?3)(?>:(?3)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?6)){3}))\])$/iD'

Căn bản:

Cho phép tên miền tên miền cục bộ và tên miền (yêu cầu ít nhất hai nhãn tên miền với TLD giới hạn ở 2-6 ký tự chữ cái).

"/^(?!.{255,})(?!.{65,}@)([!#-'*+\/-9=?^-~-]+)(?>\.(?1))*@(?!.*[^.]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?\.){1,126}[a-z]{2,6}$/iD"

Ngôn ngữ của quỷ là gì vậy ?? Tôi thấy một /Dlá cờ, và bạn đã trích dẫn nó với các trích dẫn duy nhất nhưng cũng được sử dụng dấu gạch chéo để phân định mẫu? Đó không phải là Perl, và nó không thể là PCRE. Do đó có phải là PHP không? Tôi tin rằng đó là ba người duy nhất cho phép đệ quy như thế nào (?1).
tchrist

Đó là trong PHP, sử dụng PCRE. Dấu gạch chéo chỉ được sử dụng để phân định các ký tự đặc biệt như dấu ngoặc đơn, dấu ngoặc vuông và tất nhiên là dấu gạch chéo và dấu ngoặc đơn. Cờ / D, nếu bạn không biết, là để ngăn dòng mới được thêm vào cuối chuỗi, điều này sẽ được cho phép theo cách khác.
MichaelRushton

9

Kỳ lạ là bạn "không thể" cho phép TLD 4 ký tự. Bạn đang cấm mọi người từ .info.name , và giới hạn độ dài dừng .travel.museum , nhưng vâng, chúng ít phổ biến hơn TLD 2 ký tự và TLD 3 ký tự.

Bạn cũng nên cho phép bảng chữ cái viết hoa. Hệ thống email sẽ bình thường hóa phần cục bộ và phần tên miền.

Đối với regex của bạn về phần tên miền, tên miền không thể bắt đầu bằng '-' và không thể kết thúc bằng '-'. Dash chỉ có thể ở giữa.

Nếu bạn đã sử dụng thư viện PEAR, hãy kiểm tra chức năng thư của họ (quên tên chính xác / thư viện). Bạn có thể xác thực địa chỉ email bằng cách gọi một chức năng và xác thực địa chỉ email theo định nghĩa trong RFC822.


2
@Joseph Yee: Không phải RFC 822 là một ngày?
tchrist

8
public bool ValidateEmail(string sEmail)
{
    if (sEmail == null)
    {
        return false;
    }

    int nFirstAT = sEmail.IndexOf('@');
    int nLastAT = sEmail.LastIndexOf('@');

    if ((nFirstAT > 0) && (nLastAT == nFirstAT) && (nFirstAT < (sEmail.Length - 1)))
    {
        return (Regex.IsMatch(sEmail, @"^[a-z|0-9|A-Z]*([_][a-z|0-9|A-Z]+)*([.][a-z|0-9|A-Z]+)*([.][a-z|0-9|A-Z]+)*(([_][a-z|0-9|A-Z]+)*)?@[a-z][a-z|0-9|A-Z]*\.([a-z][a-z|0-9|A-Z]*(\.[a-z][a-z|0-9|A-Z]*)?)$"));
    }
    else
    {
        return false;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.