วิธีการตรวจสอบที่อยู่อีเมลโดยใช้การแสดงออกปกติ?


3311

ในช่วงหลายปีที่ผ่านมาฉันได้พัฒนานิพจน์ปกติที่ตรวจสอบความถูกต้องของที่อยู่อีเมล MOST อย่างถูกต้องโดยสมมติว่าพวกเขาไม่ได้ใช้ที่อยู่ IP เป็นส่วนหนึ่งของเซิร์ฟเวอร์

ฉันใช้มันในโปรแกรม PHP หลายโปรแกรมและทำงานได้เกือบตลอดเวลา อย่างไรก็ตามบางครั้งฉันได้รับการติดต่อจากบุคคลที่มีปัญหากับเว็บไซต์ที่ใช้งานและฉันต้องทำการปรับเปลี่ยน (ล่าสุดฉันรู้ว่าฉันไม่อนุญาต TLD 4 ตัวอักษร)

นิพจน์ทั่วไปที่ดีที่สุดที่คุณมีหรือเคยเห็นเพื่อยืนยันอีเมลคืออะไร

ฉันได้เห็นวิธีแก้ปัญหาหลายอย่างที่ใช้ฟังก์ชั่นที่ใช้นิพจน์สั้น ๆ หลายอัน แต่ฉันอยากได้นิพจน์ที่ซับซ้อนยาว ๆ ในฟังก์ชั่นที่เรียบง่ายแทนที่จะใช้นิพจน์สั้น ๆ หลายอันในฟังก์ชั่นที่ซับซ้อนกว่า



5
regex ที่สามารถตรวจสอบว่า IDNA มีรูปแบบที่ถูกต้องไม่พอดีกับ stackexchange (กฎเกี่ยวกับการบัญญัติยอมรับการกินจริงๆคดเคี้ยวและโดยเฉพาะอย่างยิ่งไม่เหมาะสำหรับการประมวลผลการ regex)
Jasen


Regexes อาจมีตัวแปรเช่นเดียวกับในบางกรณีอีเมลต่อต้านอาจมีช่องว่างและในบางครั้งอาจไม่มีช่องว่าง
Ṃųỻịgǻňạcểơửṩ

คำตอบ:


2439

อย่างเต็มที่ RFC 822 regex ตามมาตรฐานจะไม่มีประสิทธิภาพและปิดบังเพราะความยาวของมัน โชคดีที่ RFC 822 ถูกแทนที่สองครั้งและสเปคปัจจุบันที่อยู่อีเมลเป็นRFC 5322 RFC 5322 นำไปสู่การ regex ที่สามารถเข้าใจได้หากศึกษาไม่กี่นาทีและมีประสิทธิภาพเพียงพอสำหรับการใช้งานจริง

สามารถพบ regex ที่เข้ากันได้กับ RFC 5322 หนึ่งรายการที่ด้านบนของหน้าเว็บที่http://emailregex.com/แต่ใช้รูปแบบที่อยู่ IP ที่ลอยไปรอบ ๆ อินเทอร์เน็ตพร้อมจุดบกพร่องที่อนุญาต00ค่าทศนิยมไบต์ที่ไม่ได้ลงนามใด ๆ ใน ที่อยู่ที่มีการคั่นด้วยจุดซึ่งผิดกฎหมาย ส่วนที่เหลือของมันดูเหมือนจะสอดคล้องกับไวยากรณ์ RFC 5322 และผ่านการทดสอบหลายอย่างโดยใช้grep -Poรวมถึงชื่อโดเมนกรณีที่อยู่ IP คนที่ไม่ดีและชื่อบัญชีที่มีและไม่มีคำพูด

การแก้ไข00ข้อบกพร่องในรูปแบบ IP เราได้รับ regex ที่ทำงานได้อย่างรวดเร็ว (คัดลอกเวอร์ชันที่เรนเดอร์ไม่ใช่มาร์กดาวน์สำหรับโค้ดจริง)

(: [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]) +) \])

หรือ:

(?:[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])+)\])

นี่คือไดอะแกรมของfinite state machineสำหรับ regexp ด้านบนซึ่งชัดเจนกว่า regexp เอง ป้อนคำอธิบายรูปภาพที่นี่

รูปแบบที่ซับซ้อนมากขึ้นใน Perl และ PCRE (ห้องสมุด regex ใช้เช่นใน PHP) สามารถRFC แยกอย่างถูกต้อง 5322 โดยไม่ต้องผูกปม Python และ C # ก็สามารถทำได้เช่นกัน แต่พวกเขาใช้ไวยากรณ์ที่แตกต่างจากสองตัวแรก อย่างไรก็ตามหากคุณถูกบังคับให้ใช้ภาษาที่มีรูปแบบการจับคู่รูปแบบที่มีประสิทธิภาพน้อยกว่าหนึ่งในนั้นก็จะเป็นการดีที่สุดที่จะใช้ตัวแยกวิเคราะห์จริง

สิ่งสำคัญคือต้องเข้าใจว่าการตรวจสอบความถูกต้องตาม RFC ไม่ได้บอกคุณอย่างแน่นอนว่าที่อยู่นั้นมีอยู่จริงในโดเมนที่ให้มาหรือไม่หรือบุคคลที่ป้อนที่อยู่นั้นเป็นเจ้าของจริงหรือไม่ ผู้คนลงทะเบียนรายชื่อผู้ใช้อื่น ๆ ทางไปรษณีย์ตลอดเวลา การแก้ไขที่ต้องใช้การตรวจสอบความถูกต้องที่เกี่ยวข้องกับการส่งที่อยู่นั้นข้อความที่มีโทเค็นการยืนยันหมายถึงการป้อนบนหน้าเว็บเดียวกับที่อยู่

โทเค็นการยืนยันเป็นวิธีเดียวที่จะรู้ว่าคุณมีที่อยู่ของบุคคลที่ป้อน นี่คือเหตุผลที่รายชื่อผู้รับจดหมายส่วนใหญ่ใช้กลไกดังกล่าวเพื่อยืนยันการลงชื่อสมัครใช้ president@whitehouse.govท้ายที่สุดทุกคนสามารถใส่ลงไปและนั่นก็จะแยกเป็นกฎหมาย แต่ก็ไม่น่าจะเป็นคนที่ปลายอีกด้าน

สำหรับ PHP คุณไม่ควรใช้รูปแบบที่ระบุในการตรวจสอบความถูกต้องของที่อยู่อีเมลด้วย PHP วิธีที่ถูกต้องที่ฉันอ้างถึง:

มีอันตรายบางอย่างที่การใช้งานทั่วไปและการเขียนโค้ดเลอะเทอะอย่างแพร่หลายจะสร้างมาตรฐานที่แท้จริงสำหรับที่อยู่อีเมลที่มีข้อ จำกัด มากกว่ามาตรฐานทางการที่บันทึกไว้

นั่นไม่ดีไปกว่ารูปแบบที่ไม่ใช่ RFC อื่น ๆ ทั้งหมด มันเป็นไปไม่ได้เพียงพอที่จะจัดการกับสมาร์ทแม้RFC 822นับประสา RFC 5322. หนึ่งนี้ก็คือ

หากคุณต้องการที่จะได้รับแฟนซีและอวดความรู้การใช้เครื่องมือของรัฐที่สมบูรณ์ นิพจน์ทั่วไปสามารถทำหน้าที่เป็นตัวกรองพื้นฐานเท่านั้น ปัญหาเกี่ยวกับการแสดงออกปกติคือการบอกใครบางคนว่าที่อยู่อีเมลที่ถูกต้องสมบูรณ์นั้นไม่ถูกต้อง (เป็นผลบวกที่ผิด) เพราะการแสดงออกปกติของคุณไม่สามารถจัดการได้มันเป็นเพียงความหยาบคายและไม่สุภาพจากมุมมองของผู้ใช้ เอ็นจิ้นสถานะเพื่อวัตถุประสงค์สามารถตรวจสอบและแก้ไขที่อยู่อีเมลที่อาจถือว่าไม่ถูกต้องเนื่องจากจะแยกที่อยู่อีเมลตาม RFC แต่ละรายการ สิ่งนี้จะช่วยให้ได้รับประสบการณ์ที่น่าพึงพอใจมากขึ้นเช่น

ที่อยู่อีเมลที่ระบุ 'myemail @ address, com' ไม่ถูกต้อง คุณหมายถึง 'myemail@address.com' หรือเปล่า

ดูการตรวจสอบที่อยู่อีเมลรวมถึงความคิดเห็นด้วย หรือเปรียบเทียบ E-mail Address ตรวจสอบการแสดงผลปกติ

การสร้างภาพการแสดงออกปกติ

Debuggex Demo


179
คุณพูดว่า "ไม่มีนิพจน์ปกติที่ดี" เป็นการตรวจสอบที่อยู่อีเมลทั่วไปหรือแบบเฉพาะเจาะจงหรือไม่
Tomalak

37
@Tomalak: สำหรับที่อยู่อีเมลเท่านั้น ตามที่ bortzmeyer กล่าวว่า RFC นั้นซับซ้อนมาก
Luk

37
บทความบันทึกประจำวันของ linux ที่คุณพูดถึงนั้นเป็นความจริงที่ผิดหลายประการ โดยเฉพาะอย่างยิ่งโลเวลล์อย่างชัดเจนไม่ได้อ่าน errata ไปยัง RFC3696 และทำซ้ำข้อผิดพลาดบางอย่างใน RFC เวอร์ชันที่เผยแพร่ เพิ่มเติมได้ที่นี่: dominicsayers.com/isemail
Dominic Sayers

9
Jeff Atwood มี regex น่ารักในบล็อกโพสต์นี้เพื่อตรวจสอบที่อยู่อีเมลที่ถูกต้องทั้งหมด: codinghorror.com/blog/2005/02/regex-use-vs-regex-abuse.html
CMircea

5
โปรดทราบว่าข้อมูลจำเพาะ HTML5 ปัจจุบันรวมถึง regex และ ABNF สำหรับการตรวจสอบความถูกต้องของการป้อนข้อมูลประเภทอีเมลซึ่งมีข้อ จำกัด อย่างจงใจมากกว่า RFC ดั้งเดิม
ซิงโคร

746

คุณไม่ควรใช้การแสดงออกปกติเพื่อตรวจสอบที่อยู่อีเมล

ให้ใช้คลาสMailAddressแทนเช่นนี้

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

MailAddressระดับใช้ parser BNF ในการตรวจสอบที่อยู่ตามแบบมี RFC822

หากคุณวางแผนที่จะใช้ในMailAddressการตรวจสอบที่อยู่อีเมลโปรดทราบว่าวิธีการนี้ยอมรับส่วนแสดงชื่อของที่อยู่อีเมลด้วยเช่นกันและนั่นอาจไม่ใช่สิ่งที่คุณต้องการ ตัวอย่างเช่นมันยอมรับสตริงเหล่านี้เป็นที่อยู่อีเมลที่ถูกต้อง:

  • "user1@hotmail.com; user2@gmail.com"
  • "user1@hotmail.com; user2@gmail.com; user3@company.com"
  • "ชื่อที่แสดงของผู้ใช้ user3@company.com"
  • "user4 @ company.com"

ในบางกรณีเหล่านี้เฉพาะส่วนสุดท้ายของสตริงที่แยกวิเคราะห์เป็นที่อยู่ ส่วนที่เหลือก่อนหน้านั้นคือชื่อที่แสดง ในการรับที่อยู่อีเมลธรรมดาโดยไม่มีชื่อที่แสดงคุณสามารถตรวจสอบที่อยู่ปกติกับสตริงต้นฉบับของคุณ

bool isValid = false;

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

นอกจากนี้ที่อยู่ที่มีจุดเป็นจุดสิ้นสุดเช่นเดียวกับuser@company.MailAddress ก็เป็นที่ยอมรับเช่นกัน

หากคุณต้องการใช้ regex จริงๆนี่คือ :

(?: (?: \ 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 \ 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 \ \ 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 \ \ 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
คุณจะพบว่าคลาส MailAddress ใน. NET 4.0 นั้นดีกว่าการตรวจสอบที่อยู่อีเมลมากกว่ารุ่นก่อนหน้า ฉันทำการปรับปรุงที่สำคัญบางอย่างกับมัน
เจฟฟ์ทักเกอร์

7
ฉันคิดว่ามันเป็น ... ใช้งานไม่ได้ ... สำหรับรหัสที่ง่ายกว่า a @ b ไม่ตรวจสอบความถูกต้อง ar@b.com จับคู่เท่านั้นจนถึง ar @ b คอมจะไม่ตรงกัน อย่างไรก็ตามบางอย่างเช่น "ฉันเป็นฉัน" @ [10.10.10.10] ใช้งานได้! :)
Raze

5
ถูกเตือนว่าเครื่องมือตรวจสอบ Regex ที่สอดคล้องกับ RFC เหล่านี้จะให้ที่อยู่อีเมลจำนวนมากซึ่งคุณอาจไม่ต้องการยอมรับเช่น "a <body / onload = alert (' lol.com?'+document.cookies ) @aa> "ซึ่งเป็นที่อยู่อีเมลที่ถูกต้องในอีเมล :: ที่ถูกต้องของ Perl (ซึ่งใช้ regex ขนาดใหญ่นั้น) และสามารถนำไปใช้กับ XSS rt.cpan.org/Public/Bug/Display.html?id=75650
Matthew Lock

9
@MatthewLock: fake@not-a-real-domain.nameนั่นคือไม่เลวร้ายยิ่งกว่า คุณต้องไม่พึ่งพาการตรวจสอบอีเมลเพื่อป้องกัน XSS
SLAK

10
@MatthewLock: ไม่คุณต้องหลีกเลี่ยงการสืบค้น SQL (หรือยังดีกว่าใช้พารามิเตอร์) การฆ่าเชื้อนั้นไม่ใช่การป้องกันที่เหมาะสม
slaks

536

คำถามนี้ถูกถามบ่อยมาก แต่ฉันคิดว่าคุณควรถอยกลับและถามตัวเองว่าทำไมคุณถึงต้องการตรวจสอบความถูกต้องของที่อยู่อีเมล syntactically? ประโยชน์คืออะไร?

  • มันจะไม่จับตัวพิมพ์ผิดธรรมดา
  • มันไม่ได้ป้องกันคนจากการป้อนที่อยู่อีเมลที่ไม่ถูกต้องหรือที่ทำขึ้นหรือป้อนที่อยู่ของคนอื่น

หากคุณต้องการตรวจสอบว่าอีเมลถูกต้องคุณไม่มีทางเลือกนอกจากส่งอีเมลยืนยันและให้ผู้ใช้ตอบกลับ ในหลายกรณีคุณจะต้องส่งอีเมลยืนยันเพื่อเหตุผลด้านความปลอดภัยหรือเพื่อเหตุผลด้านจริยธรรม (ดังนั้นคุณจึงไม่สามารถลงชื่อสมัครใช้บริการกับผู้อื่นได้ตามต้องการ)


92
อาจเป็นการตรวจสอบที่คุ้มค่าว่าพวกเขาป้อนบางสิ่งบางอย่าง @ บางอย่างลงในฟิลด์ในการตรวจสอบฝั่งไคลเอ็นต์เพื่อตรวจจับข้อผิดพลาดง่ายๆ - แต่โดยทั่วไปแล้วคุณพูดถูก
Martin Beckett

8
มาร์ตินฉันให้ +1 กับคุณเพื่ออ่านภายหลังว่า foobar @ dk เป็นอีเมลที่ถูกต้อง มันจะไม่สวย แต่ถ้าคุณต้องการให้สอดคล้องกับ RFC และใช้สามัญสำนึกคุณควรตรวจสอบกรณีเช่นนี้และขอให้ผู้ใช้ยืนยันว่าถูกต้อง
philfreo

105
@olavk: หากมีคนพิมพ์ผิด (เช่น:) me@hotmailพวกเขาจะไม่ได้รับอีเมลยืนยันของคุณแน่นอนและพวกเขาอยู่ที่ไหน พวกเขาไม่ได้อยู่ในไซต์ของคุณอีกและพวกเขาสงสัยว่าทำไมพวกเขาจึงไม่สามารถสมัคร ที่จริงแล้วไม่มีพวกเขาไม่ใช่ - พวกเขาลืมคุณไปหมดแล้ว อย่างไรก็ตามหากคุณสามารถตรวจสอบสติพื้นฐานด้วย regex ในขณะที่พวกเขายังอยู่กับคุณพวกเขาสามารถจับข้อผิดพลาดนั้นได้ทันทีและคุณมีผู้ใช้ที่มีความสุข
nickf

5
@JacquesB: คุณสร้างจุดยอดเยี่ยม เพียงเพราะผ่านการชุมนุมต่อ RFC ไม่ได้หมายความว่ามันเป็นที่อยู่ของผู้ใช้จริงๆ มิฉะนั้นที่president@whitehouse.govอยู่เหล่านั้นทั้งหมดจะบ่งบอกถึงผู้บัญชาการทหารสูงสุดในเน็ตบุ๊ก :)
tchrist

39
ไม่จำเป็นต้องเป็นสีดำหรือสีขาว หากอีเมลผิดให้ผู้ใช้ทราบ หากผู้ใช้ยังต้องการดำเนินการต่อให้เขา อย่าบังคับให้ผู้ใช้ปฏิบัติตาม regex ของคุณ แต่ให้ใช้ regex เป็นเครื่องมือเพื่อช่วยให้ผู้ใช้ทราบว่าอาจมีข้อผิดพลาด
ninjaneer

354

ทุกอย่างขึ้นอยู่กับความถูกต้องที่คุณต้องการ สำหรับจุดประสงค์ของฉันที่ฉันพยายามเก็บสิ่งต่าง ๆ เช่นbob @ aol.com(ช่องว่างในอีเมล) หรือsteve(ไม่มีโดเมนเลย) หรือmary@aolcom(ไม่มีจุดก่อนคอม.) ฉันใช้

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

แน่นอนว่ามันจะตรงกับสิ่งที่ไม่ใช่ที่อยู่อีเมลที่ถูกต้อง แต่เป็นเรื่องของการได้รับข้อผิดพลาดทั่วไป

มีการเปลี่ยนแปลงจำนวนมากที่สามารถทำกับ regex นั้น (และบางอันอยู่ในความคิดเห็นสำหรับคำตอบนี้) แต่มันง่ายและเข้าใจง่ายและเป็นความพยายามครั้งแรกที่ดี


6
ไม่ตรงกับ foobar @ dk ซึ่งเป็นที่อยู่อีเมลที่ถูกต้องและใช้งานได้ (แม้ว่าเซิร์ฟเวอร์อีเมลส่วนใหญ่จะไม่ยอมรับหรือจะเพิ่ม
some.com

3
ใช่! ฉันจะ. ฉันขอแนะนำให้คุณลองด้วยตัวคุณเอง $ perl -le'print q{foo@bar.co.uk} = ~ /^\S+@\S+\.\S+$/? q {Y}: q {N} '
Andy Lester

7
@ Richard: จะรวมอยู่ใน. \S
David Thornley

43
JJJ: ใช่มันจะเข้ากับคนเยอะมาก มันจะจับคู่ & $ * # $ (@ $ 0 (%)) $ #.) & *) (* $ ด้วยสำหรับฉันฉันกังวลเกี่ยวกับการจับตัวพิมพ์นิ้วคลำหาที่แปลกmary@aolcomกว่าที่ฉันทำ . YMMV.
Andy Lester

5
เพียงเพื่อควบคุม@สัญญาณ: /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/ jsfiddle.net/b9chris/mXB96
Chris Moschini

338

มันขึ้นอยู่กับสิ่งที่คุณหมายถึงดีที่สุด: ถ้าคุณกำลังพูดถึงการจับที่อยู่อีเมลที่ถูกต้องใช้ต่อไปนี้:

(?:(?:\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 ) หากคุณกำลังมองหาบางอย่างที่ง่ายกว่า แต่จะจับที่อยู่อีเมลที่ถูกต้องส่วนใหญ่ลองทำสิ่งต่อไปนี้:

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

แก้ไข: จากลิงค์:

นิพจน์ทั่วไปนี้จะตรวจสอบความถูกต้องของที่อยู่ที่มีความคิดเห็นใด ๆ ที่ถูกถอดออกและแทนที่ด้วยช่องว่าง (โมดูลนี้ทำโดย)


10
ไม่ตรงกับที่อยู่ทั้งหมดบางแห่งต้องเปลี่ยนก่อน จากลิงก์: "นิพจน์ปกตินี้จะตรวจสอบความถูกต้องของที่อยู่ที่มีความคิดเห็นใด ๆ ที่ถูกถอดออกและแทนที่ด้วยช่องว่าง (โมดูลนี้ทำโดย)"
Chas Owens

47
คุณสามารถยกตัวอย่างบางส่วนemail addressที่ผิดพลาดผ่านวินาทีที่สอง แต่ถูกจับโดย regex ที่ยาวกว่าได้ไหม
Lazer

4
มาก แต่ผมไม่ได้ทำครั้งเดียวความรักมันว่าเป็น RFC 822 ตรวจสอบไม่RFC 5322หนึ่ง
tchrist

24
@Lazer ใน ..valid @ example.com จะเป็นตัวอย่างง่ายๆ คุณไม่ได้รับอนุญาตให้มีจุดที่ไม่ได้ต่อเนื่องสองจุดติดต่อกันในส่วนของพื้นที่
Randal Schwartz

5
@ Mikail perl แต่คุณไม่ควรใช้มันจริงๆ
คนดี

287

[อัพเดท] ฉันได้รวบรวมทุกอย่างที่ฉันรู้เกี่ยวกับการตรวจสอบความถูกต้องของที่อยู่อีเมล: http://isemail.infoซึ่งตอนนี้ไม่เพียง แต่ตรวจสอบความถูกต้อง แต่ยังวินิจฉัยปัญหาเกี่ยวกับที่อยู่อีเมล ฉันเห็นด้วยกับความคิดเห็นมากมายที่นี่ว่าการตรวจสอบเป็นเพียงส่วนหนึ่งของคำตอบ ดูเรียงความของฉันที่http://isemail.info/about

is_email () ยังคงเท่าที่ฉันรู้ผู้ตรวจสอบเท่านั้นที่จะบอกคุณอย่างชัดเจนว่าสตริงที่กำหนดเป็นที่อยู่อีเมลที่ถูกต้องหรือไม่ ฉันอัปโหลดเวอร์ชันใหม่ที่http://isemail.info/

ฉันเปรียบเทียบกรณีทดสอบจาก Cal Henderson, Dave Child, Phil Haack, Doug Lovell, RFC5322 และ RFC 3696 มีที่อยู่ 275 การทดสอบทั้งหมด ฉันใช้การทดสอบทั้งหมดกับผู้ตรวจสอบอิสระฟรีทุกตัวที่ฉันพบ

ฉันจะพยายามปรับปรุงหน้านี้ให้ทันสมัยอยู่เสมอเมื่อผู้คนปรับปรุงเครื่องมือตรวจสอบความถูกต้อง ขอขอบคุณที่แคล, ไมเคิล, เดฟ, พอลและฟิลเพื่อขอความช่วยเหลือและความร่วมมือของพวกเขาในการรวบรวมการทดสอบเหล่านี้และการวิจารณ์ที่สร้างสรรค์ของตรวจสอบของตัวเอง

ผู้คนควรตระหนักถึงความผิดพลาดต่อ RFC 3696 เป็นพิเศษ ตัวอย่างที่ยอมรับได้สามตัวอย่างนั้นเป็นที่อยู่ที่ไม่ถูกต้อง และความยาวสูงสุดของที่อยู่คือ 254 หรือ 256 ตัวอักษรไม่ใช่ 320


เครื่องมือตรวจสอบนี้ดูเหมือนว่าถูกต้อง [... เวลาผ่านไป ... ] หืมมดูเหมือนว่ามันจะเป็นแค่ RFC 5322 ไม่ใช่ 3693 หรือไม่ก็เอาแน่เอานอน
tchrist

1
ดีมาก. ที่นี่เราไม่เพียง แต่จะได้รับเรียงความที่ดีเรายังได้รับการทดสอบการตรวจสอบเช่นเดียวกับห้องสมุดเพื่อดาวน์โหลด คำตอบที่ดี!
bgmCoder

เครื่องมือตรวจสอบของคุณไม่รองรับ punycode (RFC 3492) name@öäü.atสามารถเป็นที่อยู่ที่ถูกต้องได้ (แปลเป็น name@xn--4ca9at.at)
Josef พูดว่า Reinstate Monica

สวัสดี @Josef คุณควรพยายามตรวจสอบname@xn--4ca9at.atเนื่องจากรหัสนี้เกี่ยวกับการตรวจสอบไม่ใช่การตีความ หากคุณต้องการเพิ่มนักแปล Punycode ฉันยินดีที่จะรับคำขอดึงที่github.com/dominicsayers/isemail
Dominic Sayers

266

ตามข้อกำหนด W3C HTML5 :

^[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])?)*$

บริบท:

ที่อยู่อีเมลที่ถูกต้องคือสตริงที่ตรงกับการผลิต ABNF […]

หมายเหตุ: ข้อกำหนดนี้เป็นการละเมิดโดยเจตนาของRFC 5322ซึ่งกำหนดไวยากรณ์สำหรับที่อยู่อีเมลที่เข้มงวดเกินไปพร้อมกัน (ก่อนอักขระ“ @”) คลุมเครือเกินไป (หลังอักขระ“ @”) และหละหลวมเกินไป ( การอนุญาตความคิดเห็นอักขระช่องว่างและสตริงที่อ้างถึงในลักษณะที่ไม่คุ้นเคยกับผู้ใช้ส่วนใหญ่) เพื่อการใช้งานจริงที่นี่

นิพจน์ทั่วไปที่เข้ากันได้กับ JavaScript และ Perl ต่อไปนี้คือการนำไปใช้งานของคำนิยามข้างต้น

/^[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
สิ่งนี้น่าสนใจ มันเป็นการละเมิด RFC แต่เป็นเรื่องจงใจและทำให้มันลำบาก ตัวอย่างในโลกแห่งความเป็นจริง: gmail จะละเว้นจุดในส่วนก่อนหน้า @ ดังนั้นหากอีเมลของคุณคือ test@gmail.com คุณสามารถส่งอีเมลเพื่อทดสอบ @ gmail.com หรือทดสอบ .... @ gmail.com ทั้งสองที่อยู่เหล่านี้คือ ไม่ถูกต้องตาม RFC แต่ใช้ได้ในโลกแห่งความจริง
valentinas

ฉันคิดว่าส่วนสุดท้ายควรเป็น '+' แทนที่จะเป็น '*': ^ [a-zA-Z0-9.! # $% & '* + / =? ^ _ `{|} ~ -] + @ [a- zA-Z0-9 -] + (?: \. [a-zA-Z0-9 -] +) + $
mmmmmm

7
@mmmmmm john.doe@localhostถูกต้อง แน่นอนในแอปพลิเคชันโลกแห่งความเป็นจริง (เช่นชุมชน) ฉันต้องการให้คุณแนะนำแทนที่ * โดย +
rabudde

3
@valentinas ที่จริงแล้ว RFC ไม่ได้แยกส่วนในท้องถิ่นเหล่านี้ แต่จะต้องมีการเสนอราคา "test...."@gmail.comใช้ได้อย่างสมบูรณ์ตาม RFC และมีความหมายเทียบเท่าtest....@gmail.comกัน
Rinke

ฉันได้รับข้อผิดพลาดขณะพยายามส่งอีเมลโดยใช้ python ผ่านรีเลย์ของ บริษัท หากฉันพยายามส่งไปยังที่อยู่ด้วย. @ หรือ .. @ ที่จริงแล้วเป็นกรณีที่มี _ @ ฉันจะลบสิ่งเหล่านั้นออกก่อนที่จะส่งแทนที่จะเชื่อว่าผู้รับจะทำเช่นนั้น
ndvo

201

ง่ายใน Perl 5.10 หรือใหม่กว่า:

/(?(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
ชอบที่จะเห็นสิ่งนี้ใน Python
tdc

4
ฉันคิดว่ามีเพียงส่วนย่อยเท่านั้นที่addrspecเกี่ยวข้องกับคำถาม ยอมรับมากกว่านั้นและส่งต่อแม้ว่าส่วนอื่น ๆ ของระบบที่ไม่พร้อมที่จะยอมรับที่อยู่ RFC5822 เต็มรูปแบบก็เหมือนกับการถ่ายภาพเป็นเท้าของคุณเอง
dolmen

3
ยอดเยี่ยม (+1) แต่ในทางเทคนิคแล้วมันไม่ใช่ regex แน่นอน ... (ซึ่งคงเป็นไปไม่ได้เนื่องจากไวยากรณ์ไม่ได้เป็นแบบปกติ)
Rinke

10
regexes หยุดเป็นปกติบางเวลาที่ผ่านมา มันเป็น Perl 'regex' ที่ถูกต้องแม้ว่า!
rjh

4
ฉันตั้งค่าการทดสอบสำหรับ regex นี้บน IDEone: ideone.com/2XFecHอย่างไรก็ตามมันไม่ยุติธรรม "สมบูรณ์แบบ" มีใครสนใจที่จะพูดสอด ฉันพลาดอะไรไปรึเปล่า?
Mike

159

ฉันใช้

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

รูปแบบใดที่ใช้ใน ASP.NET โดย RegularExpressionValidator


28
Boo! ที่อยู่ (ไม่เหมาะสม) ของฉัน!@mydomain.netถูกปฏิเสธ
Phrogz

3
ตามหน้านี้data.iana.org/TLD/tlds-alpha-by-domain.txtไม่มีโดเมนที่มีเพียงอักขระเดียวในระดับบนสุดเช่น "something.c", "something.a" นี่คือรุ่นที่ สนับสนุนอย่างน้อย 2 ตัวอักษร: "something.pl", "something.us":^\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w{2,}([-.]\\w+)*$
Tomasz Szulc

4
@Wayne Whitty คุณได้พบกับปัญหาหลักว่าจะให้บริการที่อยู่ส่วนใหญ่หรือทั้งหมดรวมถึงที่ไม่มีใครจะใช้ยกเว้นเพื่อทดสอบการตรวจสอบอีเมล
Patanjali

@TomaszSzulc เพิ่มแบ็กสแลชในคำตอบของคุณสับสนฉันเพิ่งแก้ไขและการสนับสนุนชื่อโดเมน 2 ตัวอักษรทำงานอยู่ ^ \ w + ([- +. '] \ w +) * @ \ w + ([-.] \ w +) * \. \ w {2,} ([-.] \ w +) * $
Aqib Mumtaz

2
สิ่งนี้ล้มเหลวsimon-@hotmail.comซึ่งเป็นจริงที่ถูกต้อง (ลูกค้าของเรามีที่อยู่ที่คล้ายกัน) `
Simon_Weaver

142

ไม่ทราบเกี่ยวกับที่ดีที่สุด แต่คนนี้เป็นอย่างน้อยที่ถูกต้องเป็นเวลานานเป็นที่อยู่ของพวกเขามีความคิดเห็นปล้นและถูกแทนที่ด้วยช่องว่าง

อย่างจริงจัง. คุณควรใช้ห้องสมุดที่เขียนขึ้นแล้วเพื่อตรวจสอบอีเมล วิธีที่ดีที่สุดน่าจะเป็นเพียงแค่ส่งอีเมลยืนยันไปยังที่อยู่นั้น


2
เท่าที่ฉันรู้ห้องสมุดบางแห่งก็ผิดเช่นกัน ฉันจำได้ว่า PHP PEAR มีข้อบกพร่องดังกล่าว
bortzmeyer

หน้านั้นยังมีข้อจำกัดความรับผิดชอบที่ด้านล่างเกี่ยวกับสองสิ่งจากสเป็ค ที่ regexp ไม่สนับสนุน
Chris Vest

7
นั่นเป็นข้อมูลจำเพาะ RFC 822 ไม่ใช่ข้อมูลจำเพาะRFC 5322
tchrist

12
ในที่สุดเขาพูดถูกในวิธีเดียวที่จะตรวจสอบที่อยู่อีเมลอย่างแท้จริงคือการส่งอีเมลไปที่มันและรอการตอบกลับ
Blazemonger

109

ที่อยู่อีเมลที่ฉันต้องการตรวจสอบจะถูกใช้โดยเว็บแอปพลิเคชัน ASP.NET โดยใช้ System.Net.Mail namespace เพื่อส่งอีเมลไปยังรายชื่อบุคคล ดังนั้นแทนที่จะใช้นิพจน์ทั่วไปที่ซับซ้อนมากฉันแค่พยายามสร้างอินสแตนซ์ MailAddress จากที่อยู่ MailAddress construtor จะส่งข้อยกเว้นหากที่อยู่ไม่ถูกต้อง ด้วยวิธีนี้ฉันรู้ว่าอย่างน้อยฉันก็สามารถส่งอีเมลออกไปนอกประตูได้ แน่นอนนี่คือการตรวจสอบความถูกต้องฝั่งเซิร์ฟเวอร์ แต่อย่างน้อยคุณก็ต้องมีมันอยู่ดี

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
จุดที่ดี แม้ว่าการตรวจสอบเซิร์ฟเวอร์นี้จะปฏิเสธที่อยู่ที่ถูกต้องบางอย่างก็ไม่เป็นปัญหาเนื่องจากคุณจะไม่สามารถส่งไปยังที่อยู่นี้โดยใช้เทคโนโลยีเซิร์ฟเวอร์นี้โดยเฉพาะ หรือคุณสามารถลองทำสิ่งเดียวกันโดยใช้ห้องสมุดอีเมลบุคคลที่สามที่คุณใช้แทนเครื่องมือเริ่มต้น
ผู้ใช้

ฉันชอบวิธีนี้ใช้ประโยชน์จาก. กรอบรหัสสุทธิ - ไม่มีเหตุผลในการบูรณาการล้อ มันยอดเยี่ยมมาก ง่ายสะอาดและมั่นใจว่าคุณสามารถส่งอีเมลได้จริง การทำงานที่ดี.
Cory House

... ใช่และสำหรับผู้ที่สนใจในการตรวจสอบว่ามีการดูรหัสใน Reflector - มีค่อนข้างน้อย - และมันไม่ใช่การแสดงออกปกติ!
Tom Carter

2
ข้อควรทราบ: คลาส MailAddress ไม่ตรงกับ RFC5322 หากคุณต้องการใช้สำหรับการตรวจสอบความถูกต้อง (และไม่ส่งเช่นกันซึ่งในกรณีนี้เป็นจุดที่สงสัยตามที่กล่าวไว้ข้างต้น) ดู: stackoverflow.com/questions/6023589/…
porges

เพียงปัญหาเล็กน้อย: หากคุณต้องการให้โค้ดตัวตรวจสอบความถูกต้องฝั่งเซิร์ฟเวอร์ของคุณสามารถนำมาใช้ซ้ำได้มากขึ้น (ในกรณีนี้หรือโดยทั่วไป) ฉันแนะนำให้ใช้args.Valueแทนการอ้างอิงฟิลด์เช่นtxtEmail.Textฮาร์ดโค้ด ตัวหลังจะเชื่อมโยงตัวตรวจสอบความถูกต้องของคุณกับอินสแตนซ์ควบคุมเดียวซึ่งอาจใช้ได้ตราบใดที่คุณมีฟิลด์อีเมลเดียว แต่ไม่แนะนำเป็นอย่างอื่น
pholpar

109

คำตอบที่รวดเร็ว

ใช้ regex ต่อไปนี้สำหรับการตรวจสอบความถูกต้องของอินพุต:

([-!#-'*+/-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])?)+

ที่อยู่ที่ตรงกับ regex นี้:

  • มีส่วนท้องที่ (เช่นส่วนที่อยู่ก่อนเครื่องหมาย @) ซึ่งเป็นไปตาม RFC 5321/5322 อย่างเคร่งครัด
  • มีส่วนโดเมน (เช่นส่วนหลัง @- เซ็นชื่อ) ที่เป็นชื่อโฮสต์ที่มีป้ายกำกับอย่างน้อยสองป้ายแต่ละรายการมีความยาวสูงสุด 63 อักขระ

ข้อ จำกัด ที่สองคือข้อ จำกัด ของ RFC 5321/5322

คำตอบที่ซับซ้อน

การใช้นิพจน์ทั่วไปที่จดจำที่อยู่อีเมลอาจมีประโยชน์ในสถานการณ์ต่าง ๆ : ตัวอย่างเช่นการสแกนที่อยู่อีเมลในเอกสารเพื่อตรวจสอบการป้อนข้อมูลของผู้ใช้หรือเป็นข้อ จำกัด ด้านความสมบูรณ์ของที่เก็บข้อมูล

อย่างไรก็ตามควรสังเกตว่าถ้าคุณต้องการตรวจสอบว่าที่อยู่อ้างอิงถึงกล่องจดหมายที่มีอยู่จริงหรือไม่ไม่มีการส่งข้อความไปยังที่อยู่นั้นแทน หากคุณต้องการตรวจสอบว่าที่อยู่นั้นถูกต้องทางไวยากรณ์หรือไม่คุณสามารถใช้นิพจน์ปกติได้ แต่โปรดทราบว่า""@[]เป็นที่อยู่อีเมลที่ถูกต้องตามหลักไวยากรณ์ที่แน่นอนไม่ได้อ้างถึงกล่องจดหมายที่มีอยู่

ไวยากรณ์ของที่อยู่อีเมลที่ได้รับการกำหนดในหลายRFCsสะดุดตาที่สุดRFC 822และRFC 5322 ควรมองว่า RFC 822 เป็นมาตรฐาน "ดั้งเดิม" และ RFC 5322 เป็นมาตรฐานล่าสุด ไวยากรณ์ที่กำหนดใน RFC 822 เป็นมาตรฐานที่ผ่อนปรนที่สุดและตามมาได้ จำกัด ไวยากรณ์เพิ่มเติมและเพิ่มเติมซึ่งระบบใหม่หรือบริการใหม่ควรรับรู้ไวยากรณ์ที่ล้าสมัย แต่ไม่เคยผลิตมัน

ในคำตอบนี้ผมจะพา“ที่อยู่อีเมล” หมายถึงการaddr-specที่กำหนดไว้ใน RFCs (เช่นjdoe@example.orgแต่ไม่ได้"John Doe"<jdoe@example.org>หรือsome-group:jdoe@example.org,mrx@exampel.org;)

มีปัญหาหนึ่งในการแปลไวยากรณ์ RFC เป็น regexes: ไวยากรณ์ไม่ปกติ! นี่เป็นเพราะพวกเขาอนุญาตให้มีความคิดเห็นเพิ่มเติมในที่อยู่อีเมลที่สามารถซ้อนกันได้อย่างไม่มีที่สิ้นสุดในขณะที่การทำรังไม่สิ้นสุดไม่สามารถอธิบายได้ด้วยนิพจน์ทั่วไป ในการสแกนหรือตรวจสอบที่อยู่ที่มีความคิดเห็นคุณจะต้องมีตัวแยกวิเคราะห์หรือนิพจน์ที่มีประสิทธิภาพมากขึ้น (โปรดทราบว่าภาษาอย่าง Perl มีโครงสร้างเพื่ออธิบายไวยากรณ์ฟรีตามบริบทในลักษณะคล้าย regex) ในคำตอบนี้ฉันจะไม่สนใจความคิดเห็นและพิจารณาเฉพาะการแสดงออกปกติที่เหมาะสมเท่านั้น

RFCs กำหนดไวยากรณ์สำหรับข้อความอีเมลไม่ใช่สำหรับที่อยู่อีเมลเช่นนั้น ที่อยู่อาจปรากฏในฟิลด์ส่วนหัวต่างๆและนี่คือที่ที่พวกเขาถูกกำหนดเป็นหลัก เมื่อปรากฏในที่อยู่ฟิลด์ส่วนหัวที่อยู่อาจมีช่องว่าง (ระหว่างโทเค็นคำศัพท์) ช่องว่างความคิดเห็นและแม้แต่การแบ่งบรรทัด ความหมายนี้ไม่มีความสำคัญอย่างไรก็ตาม โดยการลบช่องว่างนี้และอื่น ๆ จากที่อยู่คุณจะได้รับความหมายเทียบเท่าตัวแทนที่ยอมรับ ดังนั้นการเป็นตัวแทนยอมรับคือfirst. last (comment) @ [3.5.7.9]first.last@[3.5.7.9]

ควรใช้ไวยากรณ์ที่แตกต่างกันเพื่อจุดประสงค์ที่แตกต่างกัน หากคุณต้องการสแกนที่อยู่อีเมลในเอกสาร (อาจเก่ามาก) อาจเป็นความคิดที่ดีที่จะใช้ไวยากรณ์ตามที่กำหนดไว้ใน RFC 822 ในทางกลับกันหากคุณต้องการตรวจสอบความถูกต้องของข้อมูลผู้ใช้คุณอาจต้องการใช้ ไวยากรณ์ตามที่กำหนดไว้ใน RFC 5322 อาจยอมรับการยอมรับแบบบัญญัติเท่านั้น คุณควรตัดสินใจว่าจะใช้ไวยากรณ์ใดกับกรณีของคุณ

ฉันใช้การแสดงออกปกติ POSIX "ขยาย" ในคำตอบนี้สมมติว่าชุดอักขระที่รองรับ ASCII

RFC 822

ฉันมาถึงที่การแสดงออกปกติต่อไปนี้ ฉันขอเชิญชวนให้ทุกคนลองและทำลายมัน หากคุณพบว่ามีผลบวกปลอมหรือเป็นเท็จใด ๆ โปรดโพสต์ไว้ในความคิดเห็นและฉันจะพยายามแก้ไขนิพจน์โดยเร็วที่สุด

([^][()<>@,;:\\". \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)*]))*

ผมเชื่อว่ามัน complient อย่างเต็มที่กับ RFC 822 รวมทั้งคหบดี มันจะจดจำที่อยู่อีเมลในรูปแบบที่ยอมรับได้เท่านั้น สำหรับ regex ที่รู้จักช่องว่าง (การพับ) ให้ดูที่มาด้านล่าง

ที่มาแสดงให้เห็นว่าฉันมาถึงที่แสดงออก ฉันแสดงกฎไวยากรณ์ที่เกี่ยวข้องทั้งหมดจาก RFC ให้ตรงตามที่ปรากฏตามด้วย regex ที่เกี่ยวข้อง ที่เผยแพร่ erratum ฉันให้นิพจน์แยกต่างหากสำหรับกฎไวยากรณ์ที่ถูกต้อง (ทำเครื่องหมาย "erratum") และใช้เวอร์ชันที่อัปเดตเป็นนิพจน์ย่อยในนิพจน์ทั่วไปที่ตามมา

ตามที่ระบุในวรรค 3.1.4 ของ RFC 822 ตัวเลือก white white space อาจถูกแทรกระหว่างโทเค็นของคำศัพท์ ฉันได้ขยายการแสดงออกเพื่อรองรับกฎนี้และทำเครื่องหมายผลลัพธ์ด้วย "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

ฉันมาถึงที่การแสดงออกปกติต่อไปนี้ ฉันขอเชิญชวนให้ทุกคนลองและทำลายมัน หากคุณพบว่ามีผลบวกปลอมหรือเป็นเท็จใด ๆ โปรดโพสต์ไว้ในความคิดเห็นและฉันจะพยายามแก้ไขนิพจน์โดยเร็วที่สุด

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

ผมเชื่อว่ามัน complient อย่างเต็มที่กับ RFC 5322 รวมทั้งคหบดี มันจะจดจำที่อยู่อีเมลในรูปแบบที่ยอมรับได้เท่านั้น สำหรับ regex ที่รู้จักช่องว่าง (การพับ) ให้ดูที่มาด้านล่าง

ที่มาแสดงให้เห็นว่าฉันมาถึงที่แสดงออก ฉันแสดงกฎไวยากรณ์ที่เกี่ยวข้องทั้งหมดจาก RFC ให้ตรงตามที่ปรากฏตามด้วย regex ที่เกี่ยวข้อง สำหรับกฎที่รวมถึงช่องว่างที่ไม่เกี่ยวข้องทางความหมาย (การพับ) ฉันต้องให้ regex แยกที่ระบุว่า "(ปกติ)" ที่ไม่ยอมรับช่องว่างนี้

ฉันเพิกเฉยต่อกฎ "obs-" ทั้งหมดจาก RFC ซึ่งหมายความว่า regexes จับคู่ที่อยู่อีเมลที่สอดคล้องกับ RFC 5322 อย่างเคร่งครัดเท่านั้น หากคุณต้องจับคู่ที่อยู่ "เก่า" (เช่นเดียวกับไวยากรณ์ที่หลวมรวมถึงกฎ "obs-") คุณสามารถใช้หนึ่งใน RFC 822 regexes จากย่อหน้าก่อนหน้า

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^-~]*])

โปรดทราบว่าบางแหล่งที่มา (สะดุดตาw3c ) อ้างว่า RFC 5322 นั้นเข้มงวดเกินไปในส่วนของโลคัล (เช่นส่วนที่อยู่หน้า @ -sign) นี่เป็นเพราะ ".. ", "a..b" และ "a." มีความไม่ถูกต้องจุดอะตอมขณะที่พวกเขาอาจจะใช้เป็นชื่อกล่องจดหมาย RFC แต่ไม่อนุญาตให้มีส่วนในท้องถิ่นเช่นนี้ยกเว้นว่าพวกเขาจะต้องมีการอ้าง ดังนั้นa..b@example.netคุณควรเขียน"a..b"@example.netซึ่งเทียบเท่ากับความหมาย

ข้อ จำกัด เพิ่มเติม

SMTP (ตามที่กำหนดในRFC 5321 ) จำกัด ชุดของที่อยู่อีเมลที่ถูกต้องเพิ่มเติม (หรือจริง ๆ : ชื่อกล่องจดหมาย) ดูเหมือนว่ามีเหตุผลที่จะกำหนดไวยากรณ์ที่เข้มงวดนี้เพื่อให้สามารถใช้ที่อยู่อีเมลที่ตรงกันเพื่อส่งอีเมลได้

โดยทั่วไปแล้ว RFC 5321 จะแยกส่วน "local" เพียงอย่างเดียว (เช่นส่วนก่อน @ -sign) แต่จะเข้มงวดกว่าในส่วนของโดเมน (เช่นส่วนที่อยู่หลัง @ -sign) จะอนุญาตเฉพาะชื่อโฮสต์แทนที่ dot-atoms และตัวอักษรที่อยู่ในสถานที่ของตัวอักษรโดเมน

ไวยากรณ์ที่แสดงใน RFC 5321 นั้นอ่อนเกินไปเมื่อพูดถึงชื่อโฮสต์และที่อยู่ IP ฉันใช้เสรีภาพในการ "แก้ไข" กฎที่เป็นปัญหาโดยใช้ร่างนี้และRFC 1034เป็นแนวทาง นี่คือผลลัพธ์ของ regex

([-!#-'*+/-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^-~]+)])

โปรดทราบว่าขึ้นอยู่กับกรณีการใช้งานคุณอาจไม่ต้องการอนุญาตให้ "General-address-literal" ใน regex ของคุณ โปรดทราบว่าฉันใช้ lookahead เชิงลบ(?!IPv6:)ใน regex สุดท้ายเพื่อป้องกันส่วน "General-address-literal" เพื่อจับคู่ที่อยู่ IPv6 ที่ผิดรูปแบบ ตัวประมวลผล regex บางตัวไม่รองรับ lookahead เชิงลบ ลบซับสตริง|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+ออกจาก regex หากคุณต้องการนำส่วน "General-address-literal" ทั้งหมดออก

นี่คือที่มา:

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^-~]+)])

การตรวจสอบอินพุตของผู้ใช้

กรณีการใช้งานทั่วไปคือการตรวจสอบการป้อนข้อมูลของผู้ใช้ตัวอย่างเช่นในรูปแบบ html ในกรณีดังกล่าวมักจะเหมาะสมที่จะตัดทอนตัวอักษรที่อยู่และต้องมีป้ายกำกับอย่างน้อยสองรายการในชื่อโฮสต์ รับ RFC 5321 regex ที่ปรับปรุงแล้วจากส่วนก่อนหน้าเป็นพื้นฐานนิพจน์ผลลัพธ์จะเป็น:

([-!#-'*+/-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..b"@example.netหรือแม้กระทั่ง"a b"@example.net)

ฉันยังไม่แนะนำให้ตรวจสอบความถูกต้องอย่างชัดเจนกับรายการโดเมนระดับบนสุดตามตัวอักษรหรือแม้แต่การจำกัดความยาว (จำได้ว่า ".museum" ไม่ถูกต้อง[a-z]{2,4}) แต่ถ้าคุณต้อง:

([-!#-'*+/-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|ฯลฯ ...)

ตรวจสอบให้แน่ใจว่าได้อัปเดต regex ของคุณแล้วหากคุณตัดสินใจที่จะตรวจสอบความถูกต้องของโดเมนระดับบนสุด

ข้อควรพิจารณาเพิ่มเติม

เมื่อยอมรับเฉพาะชื่อโฮสต์ในส่วนโดเมน (หลังเครื่องหมาย @ -) regexes ด้านบนจะยอมรับเฉพาะป้ายกำกับที่มีความยาวสูงสุด 63 ตัวอักษรตามที่ควร อย่างไรก็ตามพวกเขาไม่บังคับใช้ความจริงที่ว่าชื่อโฮสต์ทั้งหมดจะต้องมีความยาวไม่เกิน 253 อักขระ (รวมถึงจุด) แม้ว่าข้อ จำกัด นี้จะพูดอย่างเคร่งครัดเป็นประจำ แต่ก็ไม่สามารถสร้าง regex ที่รวมกฎนี้ได้

ข้อควรพิจารณาอีกประการหนึ่งโดยเฉพาะอย่างยิ่งเมื่อใช้ regexes สำหรับการตรวจสอบความถูกต้องของข้อมูลคือข้อเสนอแนะต่อผู้ใช้ หากผู้ใช้ป้อนที่อยู่ที่ไม่ถูกต้องมันจะดีกว่าถ้าให้คำติชมเล็กน้อยกว่า "ที่อยู่ที่ไม่ถูกต้องทางไวยากรณ์" แบบง่าย ด้วย "วานิลลา" regexes นี้เป็นไปไม่ได้

ข้อควรพิจารณาสองข้อนี้สามารถแก้ไขได้ด้วยการแยกที่อยู่ ข้อจำกัดความยาวพิเศษของชื่อโฮสต์อาจในบางกรณีสามารถแก้ไขได้โดยใช้ regex พิเศษที่ตรวจสอบและจับคู่ที่อยู่กับทั้งสองนิพจน์

ไม่มี regexes ในคำตอบนี้เหมาะสำหรับประสิทธิภาพ หากประสิทธิภาพเป็นปัญหาคุณควรดูว่า (และวิธี) regex ที่คุณเลือกสามารถเพิ่มประสิทธิภาพได้หรือไม่


3
RFC 6532อัปเดต5322เพื่ออนุญาตและรวม UTF-8 ที่สะอาดและครบถ้วน รายละเอียดเพิ่มเติมที่นี่

ตามวิกิพีเดียดูเหมือนว่าส่วนท้องที่เมื่อมีจุดมีข้อ จำกัด 64 ตัวอักษรต่อส่วนและ RFC 5322 หมายถึงส่วนท้องถิ่นจุดที่จะตีความด้วยข้อ จำกัด ของโดเมน ตัวอย่างเช่น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.comไม่ควรตรวจสอบ ฉันขอแนะนำให้เปลี่ยนเครื่องหมาย "+" ในกลุ่มแรก (ชื่อก่อนจุดที่ไม่จำเป็น) และในกลุ่มที่สอง (ตั้งชื่อตามจุดต่อไปนี้) เป็น{1,64}
Xavi Montero

เนื่องจากความคิดเห็นมีขนาด จำกัด นี่คือ regex ที่ฉันวางแผนที่จะใช้ซึ่งเป็นจุดเริ่มต้นของคำตอบนี้รวมถึงการ จำกัด ขนาดในส่วนท้องถิ่นรวมถึงการเพิ่มเครื่องหมายสแลชก่อน "/" สัญลักษณ์ตามที่ PHP ต้องการและใน regex101.com: ใน PHP ฉันใช้:$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

ข้อควรระวัง: ด้วยเหตุผลบางอย่าง StackOverflow เพิ่มอักขระที่ซ่อนอยู่เมื่อคัดลอกจาก markdown ที่แสดงผล คัดลอกไปที่ regex101.com แล้วคุณจะเห็นจุดสีดำตรงนั้น คุณต้องลบออกและแก้ไขสตริง ... บางทีถ้ารวมอยู่ในคำตอบแล้วพวกเขาจะสามารถแก้ไขได้อย่างถูกต้อง ขออภัยในความไม่สะดวก. ฉันไม่ต้องการเพิ่มคำตอบใหม่เนื่องจากคำตอบนี้เป็นคำตอบที่เหมาะสม นอกจากนี้ฉันไม่ต้องการแก้ไขโดยตรงเว้นแต่ชุมชนคิดว่าควรรวมเข้ากับมัน
Xavi Montero

@XaviMontero Thaks ที่สนับสนุน Xavi! คุณมีการอ้างอิงถึง RFC ที่ระบุขีด จำกัด 64 อักขระบนฉลากชิ้นส่วนท้องถิ่น ถ้าเป็นเช่นนั้นฉันยินดีที่จะปรับคำตอบ
Rinke

73

มีตัวอย่างมากมายเกี่ยวกับสิ่งนี้ในเน็ต (และฉันคิดว่าแม้แต่อันเดียวที่ใช้ตรวจสอบ RFC ได้อย่างสมบูรณ์ - แต่มันยาวนับหมื่น / บรรทัดถ้าหน่วยความจำทำหน้าที่) ผู้คนมีแนวโน้มที่จะดำเนินการตรวจสอบสิ่งนี้ ทำไมไม่ตรวจสอบว่ามันมี @ และอย่างน้อยหนึ่ง และมีความยาวขั้นต่ำที่เรียบง่าย ไม่สำคัญที่จะป้อนอีเมลปลอมและยังคงตรงกับ regex ที่ถูกต้องอยู่ดี ฉันเดาว่าผลบวกปลอมจะดีกว่าผลลบเชิงลบ


1
ใช่ แต่ที่ RFC? :) [RFC ‐ 5322 – validator] นี้ ( stackoverflow.com/questions/201323/… ) มีความยาวเพียงสี่สิบบรรทัด
tchrist

14
ก. ไม่จำเป็นต้องใช้. TLD สามารถมีที่อยู่อีเมลหรืออาจมีที่อยู่ IPv6
Sijmen Mulder

1
RFC ไม่ใช่จุดจบของเรื่อง: ICANN ไม่อนุญาตโดเมน 'dotless' อีกต่อไป: icann.org/news/announcement-2013-08-30-en
Synchro

64

ในขณะที่การตัดสินใจว่าจะอนุญาตให้ใช้อักขระใดโปรดจำไว้ว่าเพื่อนที่ไม่มีเครื่องหมายและไฮเฟน ฉันไม่สามารถควบคุมความจริงที่ว่า บริษัท ของฉันสร้างที่อยู่อีเมลของฉันโดยใช้ชื่อของฉันจากระบบ HR นั่นรวมเครื่องหมายอะโพสโทรฟีในนามสกุลของฉัน ฉันไม่สามารถบอกคุณได้ว่าฉันถูกบล็อกไม่ให้ติดต่อกับเว็บไซต์กี่ครั้งเนื่องจากที่อยู่อีเมลของฉัน "ไม่ถูกต้อง"


4
นี่เป็นปัญหาที่พบบ่อยสุด ๆ ในโปรแกรมต่างๆที่ใช้สมมติฐานที่ไม่ได้รับการรับรองเกี่ยวกับสิ่งที่เป็นและไม่ได้รับอนุญาตในชื่อบุคคล เราไม่ควรตั้งสมมติฐานดังกล่าวเพียงแค่ยอมรับตัวละครที่ RFC ที่เกี่ยวข้องพูดว่าต้องมี
tchrist

4
ใช่. ฉันโกรธเป็นพิเศษกับโปรแกรมเมอร์ที่ปฏิเสธอักษรตัวใหญ่ในที่อยู่อีเมล! โง่และ / หรือขี้เกียจ
PhiLho

63

Regex นี้มาจากอีเมลของ Perl ::ไลบรารีที่ถูกต้อง ฉันเชื่อว่ามันถูกต้องที่สุดตรงกับทั้งหมด 822 และมันขึ้นอยู่กับการแสดงออกปกติในหนังสือ O'Reilly:

นิพจน์ทั่วไปที่สร้างขึ้นโดยใช้ตัวอย่างของ Jeffrey Friedl ใน Mastering Regular Expressions ( http://www.ora.com/catalog/regexp/ )

$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 คุณจะต้องเป็นผู้เชี่ยวชาญด้าน regex เพื่อทำความเข้าใจกับสิ่งที่กำลังทำอยู่
Chris McGrath

45

ตามที่คุณเขียนใน PHP ฉันแนะนำให้คุณใช้การตรวจสอบความถูกต้องของบิลด์บิวด์ PHP กับอีเมล

filter_var($value, FILTER_VALIDATE_EMAIL)

หากคุณใช้ php เวอร์ชั่นต่ำกว่า 5.3.6 โปรดระวังปัญหานี้: https://bugs.php.net/bug.php?id=53091

หากคุณต้องการข้อมูลเพิ่มเติมเกี่ยวกับวิธีการตรวจสอบความถูกต้องของการซื้อนี้ดูที่นี่: PHP filter_var FILTER_VALIDATE_EMAIL ของ PHP ทำงานจริงหรือไม่


ได้รับการโหวตขึ้นตรงสิ่งที่ฉันจะพูด ไม่ได้จัดการ IDN แต่เปลี่ยนเป็น puny code ล่วงหน้าเพื่อแก้ไขปัญหานี้ PHP> = 5.3 มี idn_to_ascii () สำหรับสิ่งนี้ หนึ่งในวิธีที่ดีที่สุดและง่ายที่สุดในการตรวจสอบอีเมล
เทย์เลอร์

43

Cal Henderson (Flickr) เขียนบทความชื่อParsing Email Adresses ใน PHPและแสดงวิธีการทำ RFC (2) 822 ที่สอดคล้องกับที่อยู่อีเมลการแยกวิเคราะห์ นอกจากนี้คุณยังจะได้รับรหัสที่มาในPHP , Python และทับทิมซึ่งเป็นซีซีได้รับใบอนุญาต


มันบอกฉันว่าa@bถูกต้อง
dsdsdsdsd

1
@dsdsdsdsd เพราะa@bถูกต้อง ... ในกรณีนี้bคือโดเมนระดับบนสุด
rink.attendant.6

42

ฉันไม่เคยกังวลกับการสร้างนิพจน์ปกติของตัวเองเพราะโอกาสที่คนอื่นจะมากับเวอร์ชั่นที่ดีกว่าอยู่แล้ว ฉันมักจะใช้regexlibเพื่อค้นหาสิ่งที่ฉันชอบ


1
สิ่งนี้ถูกตั้งค่าสถานะสำหรับความยาวและเนื้อหา แต่ก็ยังมีผลงานที่ดีด้วย 41 คะแนนและไม่ควรลบ
จะ

37

ไม่มีสิ่งที่ใช้ได้จริง ๆ
ฉันอภิปรายปัญหาบางอย่างในคำตอบของฉันมี php ไลบรารี่สำหรับการตรวจสอบที่อยู่อีเมลหรือไม่ จะกล่าวถึงยังอยู่ในการรับรู้ Regexp ของที่อยู่อีเมลยาก?

ในระยะสั้นอย่าคาดหวังว่า regex เดียวที่สามารถใช้งานได้จะทำงานได้อย่างเหมาะสม และ regex ที่ดีที่สุดจะตรวจสอบความถูกต้องของไวยากรณ์ไม่ใช่ความถูกต้องของอีเมล (jhohn@example.com นั้นถูกต้อง แต่อาจจะตีกลับได้ ... )


แก้ไขฉันถ้าฉันผิด แต่ฉันเชื่อว่า PHP ใช้รูปแบบ PCRE ถ้าเป็นเช่นนั้นคุณควรจะสามารถที่จะหัตถกรรมสิ่งที่คล้ายกับRFC 5322 รูปแบบของ
tchrist

@tchrist: ไม่แน่ใจว่า PCRE ทำตามไวยากรณ์นี้แล้วหรือยัง (ซึ่งฉันค้นพบ) ถ้าเป็นเช่นนั้นไม่แน่ใจว่า PCRE ของ PHP จับได้กับ PCRE เวอร์ชันนี้หรือไม่ ... ถ้าฉันเข้าใจไวยากรณ์นี้อย่างถูกต้องคุณสามารถใช้ PEG parser ได้ชัดเจนกว่าและสมบูรณ์กว่า regex อยู่ดี
PhiLho

PCRE ได้ทันแล้ว แต่บางที PHP ยังไม่ทันกับ PCRE ☹
tchrist

36

หนึ่งนิพจน์ทั่วไปอย่างง่ายซึ่งอย่างน้อยจะไม่ปฏิเสธที่อยู่อีเมลที่ถูกต้องใด ๆ จะตรวจสอบบางสิ่งตามด้วยเครื่องหมาย @ จากนั้นบางสิ่งตามด้วยระยะเวลาและอย่างน้อย 2 วัน มันจะไม่ปฏิเสธอะไรเลย แต่หลังจากตรวจสอบข้อมูลจำเพาะฉันไม่พบอีเมลใด ๆ ที่จะถูกต้องและถูกปฏิเสธ

อีเมล์ = ~ /.+@[^@]+\.[^@]{2,}$/


3
นี่คือสิ่งที่ฉันกำลังมองหา ไม่ จำกัด มาก แต่ให้แน่ใจว่ามีเพียง 1 @ (เนื่องจากเราแยกวิเคราะห์รายการและต้องการตรวจสอบให้แน่ใจว่าไม่มีเครื่องหมายจุลภาคหายไป) FYI คุณสามารถมี @ ทางด้านซ้ายหากอยู่ในเครื่องหมายอัญประกาศ: Valid_email_addressesแต่มันค่อนข้างขอบ
Josh

2
หลังจากใช้แล้วตระหนักว่ามันใช้งานไม่ได้ /^[^@]+@[^@]+\.[^@]{2}[^@]*$/ ตรวจสอบเครื่องหมาย 1 @ จริง ๆ regex ของคุณจะปล่อยให้หลายรายการเนื่องจาก. * ในตอนท้าย
Josh

1
ขวา. ฉันไม่ได้พยายามที่จะปฏิเสธสิ่งที่ไม่ถูกต้องทั้งหมด แต่อย่าปฏิเสธที่อยู่อีเมลที่ถูกต้อง
spig

1
มันจะดีกว่าที่จะใช้สิ่งนี้: /^[^@]+@[^@]+\.[^@]{2,4}$/ตรวจสอบให้แน่ใจว่าลงท้ายด้วยอักขระที่ไม่ใช่ @ แบบ 2 ถึง 4 ตัว @Josh ชี้ให้เห็นว่าตอนนี้อนุญาตให้มี @ พิเศษในตอนท้าย แต่คุณยังสามารถเปลี่ยนสิ่งนั้นได้เช่นกัน: /^[^@]+@[^@]+\.[^a-z-A-Z]{2,4}$/เนื่องจากโดเมนระดับบนสุดทั้งหมดเป็นอักขระ aZ คุณสามารถแทนที่4ด้วย5หรือให้ชื่อโดเมนระดับบนสุดยาวขึ้นในอนาคตเช่นกัน
FLY

@FLY, ka @ foo ผลตอบแทนที่ถูกต้อง มันควรจะเป็นไปตามมาตรฐาน?
SexyBeast

29

คุณสามารถใช้ปลั๊กอินที่ได้รับการรับรองโดย jQuery Validation

/^((([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

ดูเหมือนว่าจะทำงานได้ดี อนุญาต: a-b'c_d.e@f-g.hแต่สามารถตรวจจับการเปลี่ยนแปลงที่ไม่เหมาะสมเช่นa-b'c_d.@f-g.hและa-b'c_d.e@f-.h
dsdsdsdsd

25

สำหรับการประเมินที่ครอบคลุมมากที่สุดของการแสดงออกปกติที่ดีที่สุดสำหรับการตรวจสอบที่อยู่อีเมลโปรดดูที่ลิงค์นี้ "การเปรียบเทียบที่อยู่อีเมลเพื่อตรวจสอบนิพจน์ปกติ "

นี่คือนิพจน์ยอดนิยมปัจจุบันเพื่อการอ้างอิง:

/^([\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: ลิงค์นั้นไม่ถูกต้องจริงๆ คำแถลงว่าจะไม่มีรูปแบบที่สมบูรณ์แบบสำหรับการตรวจสอบที่อยู่อีเมลเป็นความผิดอย่างชัดแจ้ง คุณสามารถทำได้แต่คุณต้องตรวจสอบให้แน่ใจว่าคุณทำตาม RFC จนถึงตัวอักษร และคุณต้องเลือก RFC ที่ถูกต้องด้วย
tchrist

"ดีที่สุด" ตอนนี้ใช้ไม่ได้กับ java regex - แม้หลังจากหนีออกมาและแปลงสตริงอย่างเหมาะสม
Eric Chen

23

ไม่ต้องพูดถึงว่าชื่อ (จีน, อาหรับ, กรีก, ฮิบรู, ซีริลลิและอื่น ๆ ) โดเมนที่ไม่ใช่ละตินจะต้องได้รับอนุญาตในอนาคตอันใกล้ ทุกคนมีการเปลี่ยนแปลง regex อีเมลที่ใช้เพราะตัวละครเหล่านั้นก็จะไม่ได้รับการคุ้มครองโดยมิได้[a-z]/i \wพวกเขาทั้งหมดจะล้มเหลว

ท้ายที่สุดวิธีที่ดีที่สุดในการตรวจสอบความถูกต้องของที่อยู่อีเมลยังคงเป็นการส่งอีเมลไปยังที่อยู่ที่เป็นปัญหาเพื่อตรวจสอบความถูกต้องของที่อยู่อีเมล หากที่อยู่อีเมลเป็นส่วนหนึ่งของการตรวจสอบผู้ใช้ (ลงทะเบียน / เข้าสู่ระบบ / ฯลฯ ) จากนั้นคุณสามารถรวมเข้ากับระบบการเปิดใช้งานของผู้ใช้ เช่นส่งอีเมลพร้อมลิงก์พร้อมรหัสเปิดใช้งานที่ไม่ซ้ำกันไปยังที่อยู่อีเมลที่ระบุและอนุญาตเฉพาะการเข้าสู่ระบบเมื่อผู้ใช้เปิดใช้งานบัญชีที่สร้างขึ้นใหม่โดยใช้ลิงก์ในอีเมล

หากวัตถุประสงค์ของ regex เพียงแจ้งผู้ใช้ใน UI อย่างรวดเร็วว่าที่อยู่อีเมลที่ระบุไม่เหมือนกับในรูปแบบที่ถูกต้องดีที่สุดคือตรวจสอบว่าตรงกับ regex ต่อไปนี้หรือไม่:

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

เรียบง่ายเหมือนที่ ทำไมบนโลกนี้คุณถึงสนใจเกี่ยวกับตัวละครที่ใช้ในชื่อและโดเมน? เป็นความรับผิดชอบของลูกค้าในการป้อนที่อยู่อีเมลที่ถูกต้องไม่ใช่ของเซิร์ฟเวอร์ แม้ว่าลูกค้าจะป้อนที่อยู่อีเมลที่ถูกต้องตามหลักไวยากรณ์aa@bb.ccแต่ก็ไม่ได้รับประกันว่าจะเป็นที่อยู่อีเมลที่ถูกต้อง ไม่มีใครสามารถครอบคลุม regex


4
ฉันเห็นด้วยว่าการส่งข้อความการตรวจสอบความถูกต้องมักเป็นวิธีที่ดีที่สุดสำหรับสิ่งประเภทนี้การแก้ไขที่ถูกต้องและถูกต้องจะไม่เหมือนกัน ฉันหงุดหงิดเมื่อฉันพิมพ์อีเมลของฉันสองครั้งเพื่อ "ยืนยัน" ราวกับว่าฉันไม่สามารถดูสิ่งที่ฉันพิมพ์ได้ ฉันคัดลอกเฉพาะอันแรกไปที่สองต่อไปดูเหมือนว่าจะมีการใช้มากขึ้นเรื่อย ๆ
PeteT

ตกลง! แต่ regex นี้ฉันไม่คิดว่าถูกต้องเพราะอนุญาตspacesหลังจาก@.เช่น test@test.ca com netถือว่าเป็นอีเมลที่ถูกต้องโดยใช้ regex ข้างต้นที่มันควรจะกลับมาไม่ถูกต้อง
CB4

20

ข้อมูลจำเพาะ HTML5 ให้เห็น regex ง่ายสำหรับการตรวจสอบที่อยู่อีเมล:

/^[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])?)*$/

นี้จงใจไม่ปฏิบัติตามRFC 5322

หมายเหตุ:ข้อกำหนดนี้เป็นการละเมิดโดยเจตนาของRFC 5322ซึ่งกำหนดไวยากรณ์สำหรับที่อยู่อีเมลที่เข้มงวดเกินไปพร้อมกัน (ก่อน@อักขระ), คลุมเครือเกินไป (หลัง@ตัวอักษร) และหละหลวมเกินไป (อนุญาตความคิดเห็นอักขระช่องว่าง และอ้างถึงสตริงในลักษณะที่ไม่คุ้นเคยกับผู้ใช้ส่วนใหญ่) เพื่อการใช้งานจริงที่นี่

ความยาวทั้งหมดอาจจะมีการ จำกัด 254 ตัวอักษรต่อRFC 3696 คหบดี 1690


คำตอบที่ดีที่สุด! นี่คือลิงก์ไปยังคำแนะนำ w3: w3.org/TR/html5/forms.html#valid-e-mail-address regex นี้ใช้กับเบราว์เซอร์จำนวนมาก
Ryan Taylor

3
นี่ไม่ใช่คำตอบที่ดีที่สุด! invalid@emailaddressรูปแบบนี้ตรงนี้อยู่ไม่ถูกต้องในเครือ: ฉันจะขอเตือนและทดสอบมากก่อนที่คุณจะใช้!
Sheridan

@Sheridan หากคุณคิดว่ามีปัญหากับสเป็ค HTML5 คุณสามารถแจ้งปัญหาได้ที่นี่: github.com/w3c/html/issues
Luna

สิ่งนี้ไม่ได้เพิ่มมากไปกว่าstackoverflow.com/a/8829363และ IMHO น่าจะดีกว่าเป็นการแก้ไขหรือแสดงความคิดเห็น

example @ localhost นั้นถูกต้อง แต่สำหรับแอปพลิเคชันในโลกแห่งความเป็นจริงคุณอาจต้องการบังคับใช้ส่วนขยายโดเมนสิ่งที่คุณต้องทำคือเปลี่ยน * สุดท้ายเป็น + เพื่อให้บรรลุสิ่งนี้ (เปลี่ยนส่วนของรูปแบบจาก 0+ เป็น 1+ )
Mitch Satchwell

15

เพื่อการสาธิตที่ชัดเจนสัตว์ประหลาดต่อไปนี้ค่อนข้างดี แต่ก็ยังไม่รู้จักที่อยู่อีเมลที่ถูกต้องตาม syntactically ทั้งหมด: มันรับรู้ความคิดเห็นซ้อนกันได้ลึกถึงสี่ระดับ

นี่เป็นงานสำหรับโปรแกรมวิเคราะห์คำ แต่แม้ว่าที่อยู่จะถูกต้องทางไวยากรณ์ แต่ก็อาจไม่สามารถส่งมอบได้ บางครั้งคุณต้องหันไปใช้วิธีการตามบ้านนอกของ "เฮ้คุณทุกคนดู 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

ตามมาตรฐานอย่างเป็นทางการRFC 2822 regex อีเมลที่ถูกต้องคือ

(?:[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])+)\])

ถ้าคุณต้องการใช้ใน Java มันง่ายมากจริงๆ

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 ของคุณไม่รวมตัวอักษรตัวพิมพ์ใหญ่ตัวแรกเช่นLeonardo.davinci@gmail.comซึ่งอาจสร้างความรำคาญให้ผู้ใช้บางคน ใช้อันนี้แทน:(?:[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 ขอบคุณโปรดแก้ไขคำตอบฉันจะยอมรับการเปลี่ยนแปลง
AZ_

หากฉันเพิ่มการเปลี่ยนแปลงนั้นในคำตอบของคุณจะไม่เป็น RFC 2822 อีกต่อไปดังนั้นฉันไม่รู้ว่าถูกต้องหรือไม่
Kebab Krabby

11

นี่คือ PHP ที่ฉันใช้ ฉันเลือกวิธีการแก้ปัญหานี้ในจิตวิญญาณของ "ผลบวกปลอมดีกว่าเชิงลบเท็จ" ตามที่ผู้ประกาศความเห็นคนอื่นประกาศไว้ที่นี่และเกี่ยวกับการรักษาเวลาตอบสนองของคุณและโหลดเซิร์ฟเวอร์ลง ... ไม่จำเป็นต้องเปลืองทรัพยากรเซิร์ฟเวอร์ด้วย การแสดงออกปกติเมื่อนี้จะกำจัดข้อผิดพลาดของผู้ใช้ที่ง่ายที่สุด คุณสามารถติดตามสิ่งนี้ได้เสมอโดยส่งอีเมลทดสอบหากคุณต้องการ

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

1
a) "ทรัพยากรของเซิร์ฟเวอร์เสีย" นั้นน้อยมาก แต่ถ้าคุณเอนเอียงคุณสามารถทำสิ่งนั้นกับฝั่งไคลเอ็นต์ด้วย JS b) คุณต้องการส่งจดหมายลงทะเบียนอะไรและผู้ใช้เข้าสู่ฉัน @ forgetotthedotcom? "โซลูชัน" ของคุณล้มเหลวและคุณสูญเสียผู้ใช้
johnjohn

ก) อาศัยการตรวจสอบ JS ที่จะล้มเหลวเมื่อ JavaScript ถูกปิดใช้งานไม่ได้เสียงเหมือนคิดที่ดีที่สุดอย่างใดอย่างหนึ่ง (เพียง BTW)
Auco

11

RFC 5322 มาตรฐาน:

อนุญาตให้ dot-atom local-string, local-string local-state, ล้าสมัย (dot-atom และสตริงที่อ้างถึง) ส่วนท้องถิ่น, โดเมนชื่อโดเมน (IPv4, IPv6, และ IPv4-IPv6-IPv6 address) โดเมนที่แท้จริงของโดเมน และ 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'

RFC 5321 มาตรฐาน:

อนุญาตโดเมนโลคัลส่วน dot-atom โลคัลสตริงที่ยกมาชื่อโดเมนและ (IPv4, IPv6 และที่อยู่ IPv6 ที่แมป IPv4) โดเมนตัวอักษรโดเมน

'/^(?!(?>"?(?>\\\[ -~]|[^"])"?){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'

ขั้นพื้นฐาน:

อนุญาตโดเมน dot-atom ในระบบและส่วนชื่อโดเมน (ต้องมีอย่างน้อยสองป้ายชื่อโดเมนที่มี TLD จำกัด เพียง 2-6 ตัวอักษร)

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

ภาษาปีศาจคืออะไรใน? ฉันเห็น/Dธงและคุณยกมาด้วยคำพูดเดียวยังใช้เครื่องหมายทับเพื่อกำหนดรูปแบบ? ไม่ใช่ Perl และไม่สามารถเป็น PCRE มันเป็น PHP หรือเปล่า? ฉันเชื่อว่าสิ่งเหล่านี้เป็นเพียงสามสิ่งเท่านั้นที่อนุญาตให้มีการสอบถามซ้ำ(?1)ได้
tchrist

มันอยู่ใน PHP ซึ่งใช้ PCRE เครื่องหมายทับจะใช้เพื่อคั่นอักขระพิเศษเช่นวงเล็บวงเล็บเหลี่ยมและเครื่องหมายทับแน่นอนและเครื่องหมายคำพูดเดี่ยว หากคุณไม่ทราบว่ามีการตั้งค่าสถานะ / D เพื่อป้องกันการขึ้นบรรทัดใหม่ในตอนท้ายของสตริง
MichaelRushton

9

แปลกที่คุณ "ไม่สามารถ" อนุญาต TLD ได้ 4 ตัว คุณกำลังห้ามผู้คนจาก. infoและ. nameและการจำกัดความยาวจะหยุด. travelและ.museumแต่ใช่พวกเขาพบได้น้อยกว่า TLDs 2 ตัวอักษรและ TLD 3 ตัวอักษร

คุณควรอนุญาตให้ใช้ตัวอักษรพิมพ์ใหญ่เช่นกัน ระบบอีเมลจะทำให้ปกติส่วนภายในและส่วนโดเมน

สำหรับส่วนของโดเมนของคุณชื่อโดเมนไม่สามารถขึ้นต้นด้วย '-' และไม่สามารถลงท้ายด้วย '-' ขีดกลางอยู่ระหว่างเท่านั้น

หากคุณใช้ไลบรารี PEAR ให้ตรวจสอบฟังก์ชันเมลของพวกเขา (ลืมชื่อ / ไลบรารีที่ถูกต้อง) คุณสามารถตรวจสอบที่อยู่อีเมลได้โดยการเรียกใช้ฟังก์ชันเดียวและตรวจสอบความถูกต้องของที่อยู่อีเมลตามคำจำกัดความใน RFC822


2
@Joseph Yee: ไม่ใช่ RFC 822 สักหน่อยใช่ไหม
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;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.