วิธีการตรวจสอบที่อยู่อีเมลอย่างรวดเร็ว?


338

ไม่มีใครรู้วิธีการตรวจสอบที่อยู่อีเมลใน Swift? ฉันพบรหัสนี้:

- (BOOL) validEmail:(NSString*) emailString {

    if([emailString length]==0){
        return NO;
    }

    NSString *regExPattern = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";

    NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:regExPattern options:NSRegularExpressionCaseInsensitive error:nil];
    NSUInteger regExMatches = [regEx numberOfMatchesInString:emailString options:0 range:NSMakeRange(0, [emailString length])];

    NSLog(@"%i", regExMatches);
    if (regExMatches == 0) {
        return NO;
    } else {
        return YES;
    }
}

แต่ฉันแปลมันเป็นสวิฟต์ไม่ได้


8
การแปลควรตรงไปตรงมา ส่วนใดที่ทำให้คุณมีปัญหา?
Sulthan

12
อย่าลืมสวดมนต์ว่าไม่มีผู้ใช้ของคุณที่มีหนึ่งในโดเมนระดับบนสุดใหม่ เช่น.coffee
แมทเธียสชเบา

1
@Antzi: ฉันได้ตรวจสอบกับ "someone @ gmail" และ regex ของคุณกลับเป็นจริง
Anông An

2
Regexes ไม่ทำงานสำหรับการตรวจสอบว่าผู้ใช้ป้อนที่อยู่อีเมลของพวกเขา วิธีที่ถูกต้องเพียง 100% คือการส่งอีเมล์ยืนยันการใช้งาน ดู: ฉันรู้วิธีตรวจสอบที่อยู่อีเมลจนกว่าฉันจะอ่าน RFC
mouviciel

2
นี่คือ QA ที่น่าสนใจ มันเกือบจะแน่นอน QA "ผิดพลาดที่สุด" ในเว็บไซต์ทั้งหมด คำตอบ # 1 ในปัจจุบันที่มี600 คะแนน (คืออะไร?!) นั้นผิดอย่างสิ้นเชิงในทุกวิถีทางที่เป็นไปได้ (ทุกบรรทัดมีความผิดทั้งหมดและแนวคิดและความคิดทุกอย่างผิดไป ........ !!!) คำตอบที่ได้รับการโหวตอย่างสูงอื่น ๆ หลายคำตอบนั้นเป็น "ผิดทั้งหมด", "ไม่ได้รับความนิยมอย่างมาก" หรือแตกหักง่ายและไม่ได้รวบรวม ยิ่งไปกว่านั้นในขณะที่ธรรมชาติของ Q นี้เรียกร้องให้มี "คำตอบยอดวิศวกรรม regex" คำตอบมากมาย (โหวตสูง!) คุณสมบัติที่น่ากลัววิศวกรรม regex มันเป็น QA ที่น่าสนใจจริงๆ !! ทำไม??
Fattie

คำตอบ:


769

ฉันจะใช้NSPredicate:

func isValidEmail(_ email: String) -> Bool {        
    let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"

    let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
    return emailPred.evaluate(with: email)
}

สำหรับ Swift เวอร์ชันก่อนหน้า 3.0:

func isValidEmail(email: String) -> Bool {
    let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"

    let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
    return emailPred.evaluate(with: email)
}

สำหรับ Swift เวอร์ชันก่อนหน้า 1.2:

func isValidEmail(email: String) -> Bool {
    let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"

    if let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx) {
        return emailPred.evaluateWithObject(email)
    }
    return false
}

6
จะไม่return emailTest.evaluateWithObject(testStr)ง่ายกว่าและอ่านง่ายกว่านี้อีกหรือ เปรียบเทียบกับ== trueเป็นเช่น Javascript
Sulthan

15
ไม่ได้ตรวจสอบว่ามีส่วนขยายหรือไม่ @ a ใช้ได้แล้ว :(
CularBytes

6
สิ่งนี้ไม่ถูกต้องสำหรับ test @ test ... com
Alan

3
สิ่งนี้ตรวจไม่พบอีเมล @ valid.com หรืออีเมล @ .invalid.com คำตอบด้านล่างจาก @alexcristea ทำ
Ben Sullivan

3
มันค่อนข้างตลกที่ ............ รวมทั้ง (1) regex นั้นสมบูรณ์ไม่ถูกต้องทั้งหมด(2) regex (แม้ในบริบทของสิ่งที่พยายามทำ) มีข้อผิดพลาดที่สำคัญ( 3) Swift ไม่ถูกต้อง (4) แม้จะตั้งอยู่ข้างกันสไตล์นั้นผิดทั้งหมด (5) ไม่ใช่ว่ามันจะสำคัญต่อการพักผ่อน แต่มันไม่ได้พูดถึงว่าคุณต้องแคชคำกริยา ... 6) ยังมีเหลือรหัส ("ปฏิทิน" - อะไร?) จากทุกที่มันถูกคัดลอกมาจาก
Fattie

115

การแก้ไขอัปเดตสำหรับ Swift 3:

func validateEmail(enteredEmail:String) -> Bool {

    let emailFormat = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
    let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailFormat)
    return emailPredicate.evaluate(with: enteredEmail)

}

คำตอบต้นฉบับสำหรับ Swift 2:

func validateEmail(enteredEmail:String) -> Bool {

    let emailFormat = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
    let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailFormat)
    return emailPredicate.evaluateWithObject(enteredEmail)

}

มันทำงานได้ดี


2
ครั้งแรกกับ regex ที่ถูกต้อง คนอื่น ๆ ตรวจสอบ aa @ aach เป็นจริง
netshark1000

1
@ netshark1000 มีเฉพาะ upvotes คำตอบใด ๆ จะอยู่ด้านบน :)
Azik Abdullah

NSRegularExpression นั้นใช้ง่ายกว่า NSPredicate
Guillaume Laurent

1
ไม่ได้จัดการกับเงื่อนไขสองจุดหลังจากชื่อโดเมน ลองคำตอบนี้stackoverflow.com/a/53441176/5032981
Prashant Gaikwad

@AzikAbdullah หากคุณป้อน 'abc @ gmail..com' ก็จะตรวจสอบ
Nij

110

เป็นการStringขยายชั้นเรียน

SWIFT 4

extension String {
    func isValidEmail() -> Bool {
        // here, `try!` will always succeed because the pattern is valid
        let regex = try! NSRegularExpression(pattern: "^[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])?)*$", options: .caseInsensitive)
        return regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: count)) != nil
    }
}

การใช้

if "rdfsdsfsdfsd".isValidEmail() {

}

4
countElementsได้แล้วcount
Zack Shapiro

25
xxx @ yyy return จริงไหม
Cullen SUN

1
เช่นเดียวกับ Cullen SUN foo @ bar return true
Rémy Virin

3
user @ host without .tld ยังเป็นที่อยู่อีเมลที่ถูกต้องเช่น root @ localhost
พุธ

1
โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

64

หากคุณกำลังมองหาวิธีการแก้ปัญหาที่สะอาดและง่ายต่อการทำเช่นนี้คุณควรจะดูที่https://github.com/nsagora/validation-components

มันมีภาคการตรวจสอบอีเมลซึ่งเป็นเรื่องง่ายที่จะรวมในรหัสของคุณ:

let email = "test@example.com"
let rule = EmailValidationPredicate()
let isValidEmail = rule.evaluate(with: email)

ด้านหลังประทุนจะใช้ RFC 5322 reg ex ( http://emailregex.com ):

let regex = "(?:[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}" +
    "~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\" +
    "x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\p{L}0-9](?:[a-" +
    "z0-9-]*[\\p{L}0-9])?\\.)+[\\p{L}0-9](?:[\\p{L}0-9-]*[\\p{L}0-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]?|[\\p{L}0-9-]*[\\p{L}0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21" +
    "-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"

3
ว้าวไม่รู้เกี่ยวกับ emailregex.com มันเจ๋งนะ!
ซามูเอล Ev

2
ในที่สุดหนึ่งที่กรองอีเมล @. email.com
เบ็นซัลลิแวน

มันทำงานได้อย่างแม่นยำ - abcd@abcd.com มันไม่ผ่านการตรวจสอบ abc @ abc
Anil Gupta

Ah, ในที่สุด .. : D
Ümañgßürmån

39

นี่คือทางออกที่สมเหตุสมผล:

"ทางออกที่เหมาะสม"

ใช้และทดสอบมานานหลายปีในแอปมากมายที่มีปริมาณมาก

1 - หลีกเลี่ยงข้อผิดพลาด regex ที่น่ากลัวมากมายที่คุณมักจะเห็นในคำแนะนำเหล่านี้

2 - ไม่อนุญาตให้ใช้อีเมลที่โง่เช่น "x @ x" ซึ่งคิดว่าถูกต้องภายใต้ RFC บางตัว แต่มันโง่มากไม่สามารถใช้เป็นอีเมลได้และพนักงานฝ่ายสนับสนุนของคุณจะปฏิเสธทันที บริการจดหมาย (mailchimp, google, aws ฯลฯ ) เพียงปฏิเสธ หาก (ด้วยเหตุผลบางอย่าง) คุณต้องการโซลูชันที่อนุญาตให้ใช้สตริงเช่น 'x @ x' ให้ใช้โซลูชันอื่น

3 - รหัสนั้นมากมาก ๆ เป็นที่เข้าใจได้มาก

4 - เป็น KISS เชื่อถือได้และทดสอบเพื่อทำลายแอพเชิงพาณิชย์ที่มีผู้ใช้จำนวนมาก

5 - จุดเทคนิคการวินิจฉัยเป็นทั่วโลกเช่นแอปเปิ้ลกล่าวว่ามันควรจะเป็น (ดูออกสำหรับข้อเสนอแนะรหัสซึ่งไม่ได้นี้)

let __firstpart = "[A-Z0-9a-z]([A-Z0-9a-z._%+-]{0,30}[A-Z0-9a-z])?"
let __serverpart = "([A-Z0-9a-z]([A-Z0-9a-z-]{0,30}[A-Z0-9a-z])?\\.){1,5}"
let __emailRegex = __firstpart + "@" + __serverpart + "[A-Za-z]{2,8}"
let __emailPredicate = NSPredicate(format: "SELF MATCHES %@", __emailRegex)

extension String {
    func isEmail() -> Bool {
        return __emailPredicate.evaluate(with: self)
    }
}

extension UITextField {
    func isEmail() -> Bool {
        return self.text!.isEmail()
    }
}

มันง่ายมาก

คำอธิบาย:

ในคำอธิบายต่อไปนี้ "OC" หมายถึงอักขระธรรมดา - ตัวอักษรหรือตัวเลข

__ ก่อนอื่น ... ต้องเริ่มต้นและจบด้วย OC สำหรับตัวละครที่อยู่ตรงกลางคุณสามารถมีตัวละครบางตัวเช่นขีดล่าง แต่จุดเริ่มต้นและจุดสิ้นสุดจะต้องเป็น OC (อย่างไรก็ตามมันก็โอเคที่จะมีเพียงหนึ่ง OC และนั่นคือตัวอย่างเช่น: j@blah.com)

__serverpart ... คุณมีส่วนต่างๆเช่น "blah" ซึ่งการทำซ้ำ (ดังนั้นประเภทของสิ่งของ mail.city.fcu.edu) ส่วนต่างๆจะต้องเริ่มต้นและสิ้นสุดด้วย OC แต่ตรงกลางคุณสามารถมีเครื่องหมายขีดกลาง "-" (หากคุณต้องการอนุญาตให้มีตัวละครที่ผิดปกติอื่น ๆในนั้นอาจขีดเส้นใต้เพียงแค่เพิ่มก่อนที่จะรีบ) มันก็โอเคที่จะมีส่วนที่เป็นเพียงหนึ่ง OC (เช่นเดียวกับ joe@w.campus.edu) คุณสามารถมีได้สูงสุดห้าส่วนคุณต้องมีหนึ่งส่วน สุดท้ายTLD (เช่น .com) เป็นอย่างเคร่งครัด 2-8 ในขนาดเห็นได้ชัดว่าเพียงแค่เปลี่ยน "8" ตามต้องการโดยแผนกสนับสนุนของคุณ


สำคัญ !

คุณต้องทำให้ภาคแสดงเป็นโลกอย่าสร้างมันทุกครั้ง

โปรดทราบว่านี่เป็นสิ่งแรกที่ Apple กล่าวถึงเกี่ยวกับปัญหาทั้งหมดในเอกสาร

มันน่าประหลาดใจมากเมื่อคุณเห็นข้อเสนอแนะที่ไม่แคชเพรดิเคต


1
รองรับ TLD ใหม่เช่น. engineer หรือไม่
โรมัน

สวัสดี @ Roman - สังเกตว่ามีการระบุไว้อย่างชัดเจนว่า "ในที่สุด TLD (.com หรือสิ่งที่คล้ายกัน) จะมีตัวอักษรอย่างน้อย 2 ถึง 8 ตัว" ที่ดูแลมัน คุณสามารถเปลี่ยน "8" เป็นค่าที่คุณต้องการ (สำหรับตอนนี้ใน บริษัท ขนาดใหญ่หลายแห่งฝ่ายบริการลูกค้าจะปฏิเสธ TLD ที่มีความยาวเป็นเพียงกลฉ้อฉล แต่ต่อไปนี้เป็นการตัดสินใจของคุณใช้ "8" หรือมูลค่าที่คุณต้องการ)
Fattie

2
สำหรับจุด (4): คุณทดสอบกับผู้ใช้จำนวนมากได้อย่างไร คุณติดตามผู้ใช้ที่ไม่สามารถลงทะเบียนกับแอพเชิงพาณิชย์ได้เพราะ regex ป้องกันไม่ให้พวกเขาใช้ที่อยู่อีเมลของพวกเขา? "สมเหตุสมผล" เท่านั้นที่ควรจะเป็นสิ่งที่ spec (RFC) ระบุหรือหากไม่สามารถทำได้สิ่งที่ผ่อนคลายมากขึ้น แต่ครอบคลุมทุกอย่างจาก spec หากผู้ใช้ไม่ได้รับอนุญาตให้ป้อน x @ x พวกเขาจะป้อน garbage@example.com ซึ่งจะผ่าน / regex ของคุณ
thetrutz

สวัสดี @thetrutz "garbage@example.com" เป็นที่อยู่อีเมลปกติอย่างยิ่ง RFC รวมถึงความโง่เขลาทางทฤษฎีเช่น "x @ x" ลูกค้าเชิงพาณิชย์ที่แท้จริงที่คุณหรือฉันเคยทำงานจะพูดว่า "ไม่อนุญาตให้ใช้" (โปรดทราบว่าในธุรกิจขนาดใหญ่ในโลกแห่งความจริงมีข้อ จำกัดมากมายเกินกว่าที่ร่างคร่าวๆของฉันที่นี่เพราะฉันพูดถึงในความคิดเห็นข้างต้นกับโรมัน) ประโยคสุดท้ายของคุณสับสน - แน่นอนว่า "อีเมลที่ใช้งานไม่ได้" จะผ่าน การทดสอบในท้องถิ่น? คุณหมายถึงอะไร เห็นได้ชัดว่าอีเมลมีการยืนยันในที่สุดผ่านระบบ "ยืนยันอีเมลของคุณ"
Fattie

ใน Swift จำเป็นต้องเพิ่มประสิทธิภาพทุกขั้นตอนเพราะเรามักจะใช้ภาษานี้ในฝั่งเซิร์ฟเวอร์
Nicolas Manzini

25

นี่คือฟิวส์ของคำตอบที่ได้รับการโหวตมากที่สุดสองข้อด้วย regex ที่ถูกต้อง: ส่วนขยาย String โดยใช้เพรดิเคตเพื่อให้คุณสามารถเรียกใช้ string.isEmail

    extension String {
        var isEmail: Bool {
           let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,20}"            
           let emailTest  = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
           return emailTest.evaluateWithObject(self)
        }
    }

19

วิธีที่ง่ายที่สุดใน Swift 5

extension String {
    var isValidEmail: Bool {
        NSPredicate(format: "SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}").evaluate(with: self)
    }
}

ตัวอย่าง

"kenmueller0@gmail.com".isValidEmail

ผลตอบแทน ...

true

2
จุดที่ต้องทำซ้ำคำตอบซ้ำแล้วซ้ำอีกคืออะไร? ซึ่งไม่ขึ้นอยู่กับฟีเจอร์ Swift 5 ใด ๆ
rommex

17

ฉันขอแนะนำให้ใช้เป็นส่วนเสริมของ String:

extension String {    
    public var isEmail: Bool {
        let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)

        let firstMatch = dataDetector?.firstMatch(in: self, options: NSRegularExpression.MatchingOptions.reportCompletion, range: NSRange(location: 0, length: length))

        return (firstMatch?.range.location != NSNotFound && firstMatch?.url?.scheme == "mailto")
    }

    public var length: Int {
        return self.characters.count
    }
}

และใช้มัน:

if "hodor@gameofthrones.com".isEmail { // true
    print("Hold the Door")
}

1
โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

อัปเดต Swift 4: String ส่วนขยาย {public var isEmail: Bool {ให้ dataDetector = ลอง? NSDataDetector (ชนิด: NSTextCheckingResult.CheckingType.link.rawValue) ให้ firstMatch = dataDetector? .firstMatch (ใน: ตัวเลือกตัวเลือก: NSRegularExpression.MatchingOptions.reportCompletion ช่วง: NSRange (ตำแหน่ง: 0 ความยาว: นับ) range.location! = NSNotFound && firstMatch? .url? .scheme == "mailto")}
Duan Nguyen

15

นี่เป็นเวอร์ชั่นที่อัปเดตสำหรับ Swift 2.0 - 2.2

 var isEmail: Bool {
    do {
        let regex = try NSRegularExpression(pattern: "^[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])?)*$", options: .CaseInsensitive)
        return regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count)) != nil
    } catch {
        return false
    }
}

8
foo @ bar ให้ผลตอบแทนจริงหรือ?!
Rémy Virin

2
ตรวจสอบความถูกต้อง aa @ aach ถึงจริง
netshark1000

4
นั่นเป็นเพราะ RFC ตรวจสอบความถูกต้องของที่อยู่อีเมลเหล่านี้เป็นจริง;)
dulgan

โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

เป็นเรื่องที่ผิด / แย่มากที่จะไม่ทำเพรดิเคต มันเป็นสิ่งแรกที่ Apple พูดเกี่ยวกับปัญหาใน doco ความผิดพลาดที่เห็นได้ชัดทำโดยคำตอบส่วนใหญ่ในหน้า
Fattie

9

มีคำตอบที่ถูกต้องมากมายที่นี่ แต่ "regex" จำนวนมากไม่สมบูรณ์และอาจเกิดขึ้นได้ว่าอีเมลเช่น: "name @ domain" ส่งผลให้อีเมลที่ถูกต้อง แต่ไม่ใช่ นี่คือทางออกที่สมบูรณ์:

extension String {

    var isEmailValid: Bool {
        do {
            let regex = try NSRegularExpression(pattern: "(?:[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])+)\\])", options: .CaseInsensitive)
            return regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count)) != nil
        } catch {
            return false
        }
    }
}

ทำงานไม่ถูกต้องจะช่วยให้คุณเพิ่มช่องว่างหลังจากโดเมน
Juan Boero

โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

@Fattie โต้แย้งคำสั่งของคุณ ความคิดเห็นของคุณไร้ประโยชน์มากแนะนำการปรับปรุงเสนอการแก้ไข การพูดผิดอย่างสิ้นเชิงนั้นโง่มากและรองรับความคิดที่ใกล้ชิด
Andrea.Ferrando

"มีคำตอบที่ถูกต้องมากมายที่นี่"ประโยคนั้นไม่ถูกต้องอย่างน่าทึ่ง :) :)
464

8

นี่คือวิธีการตามrangeOfString:

class func isValidEmail(testStr:String) -> Bool {
    let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
    let range = testStr.rangeOfString(emailRegEx, options:.RegularExpressionSearch)
    let result = range != nil ? true : false
    return result
}

หมายเหตุ: ความยาว TLD ที่อัปเดต

นี่คือ RegEx ที่ชัดเจนสำหรับอีเมลตาม RFC 5322 โปรดทราบว่านี่ไม่ได้ใช้ดีที่สุดเพราะจะตรวจสอบไวยากรณ์พื้นฐานของที่อยู่อีเมลเท่านั้นและไม่ได้ตรวจสอบว่ามีโดเมนระดับบนสุดอยู่แล้ว

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

ดูRegular-Expressions.infoสำหรับข้อมูลที่สมบูรณ์เพิ่มเติมเกี่ยวกับอีเมล RegExs

โปรดทราบว่าไม่มีการหลบหนีตามที่ต้องการโดยภาษาเช่น Objective-C หรือ Swift


1
emailRegEx ที่คุณใช้นั้นผิดปกติ ใช้สำหรับความยาว TLD 2 ถึง 4 ตัวอักษรเท่านั้นในขณะที่โดเมนอย่างนั้น.engineerมีอยู่จริง
Antzi

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

ทำไมโอ๋ทำไมไม่เพียงแค่ลบคำตอบแล้ว? มีเหตุผลอะไรที่เป็นไปได้ที่จะเก็บไว้ที่นี่
Fattie

7

ฉันชอบใช้ส่วนขยายสำหรับสิ่งนั้น นอกจากนี้ URL นี้http://emailregex.comสามารถช่วยคุณทดสอบว่า regex นั้นถูกต้องหรือไม่ ในความเป็นจริงเว็บไซต์มีการใช้งานที่แตกต่างกันสำหรับบางภาษาการเขียนโปรแกรม ผมร่วมการดำเนินงานของฉันสำหรับสวิฟท์ 3

extension String {
    func validateEmail() -> Bool {
        let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
        return NSPredicate(format: "SELF MATCHES %@", emailRegex).evaluate(with: self)
    }
}

มีปัญหาเล็กน้อย .. คุณสามารถมีตัวอย่างเช่น .. blah @ .abc มีจุดแปลกมี
Fattie

5

สำหรับ swift 2.1: สามารถทำงานได้อย่างถูกต้องกับอีเมล foo @ bar

extension String {
    func isValidEmail() -> Bool {
        do {
            let regex = try NSRegularExpression(pattern: "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}", options: .CaseInsensitive)
            return regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count)) != nil
        } catch {
                return false
        }
    }
}

1
ดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน เท่าที่ฉันเข้าใจคุณสามารถละเว้น 'AZ' (อักษรตัวใหญ่) เนื่องจากคุณมีตัวเลือก. CaseInsensitive set ต่อไป ...
AZOM

โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

5

การใช้ Swift 4.2

extension String {
    func isValidEmail() -> Bool {
        let regex = try? NSRegularExpression(pattern: "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$", options: .caseInsensitive)
        return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.count)) != nil
    }
    func isValidName() -> Bool{
        let regex = try? NSRegularExpression(pattern: "^[\\p{L}\\.]{2,30}(?: [\\p{L}\\.]{2,30}){0,2}$", options: .caseInsensitive)

        return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.count)) != nil
    } }

และใช้แล้ว

if (textField.text?.isValidEmail())! 
    {
      // bla bla
    }
else 
    {

    }

4

นี่เป็นเวอร์ชั่นใหม่สำหรับ"THE REASONABLE SOLUTION"โดย @Fattie ทดสอบกับ Swift 4.1 ในไฟล์ใหม่ที่เรียกว่าString+Email.swift:

import Foundation

extension String {
    private static let __firstpart = "[A-Z0-9a-z]([A-Z0-9a-z._%+-]{0,30}[A-Z0-9a-z])?"
    private static let __serverpart = "([A-Z0-9a-z]([A-Z0-9a-z-]{0,30}[A-Z0-9a-z])?\\.){1,5}"
    private static let __emailRegex = __firstpart + "@" + __serverpart + "[A-Za-z]{2,6}"

    public var isEmail: Bool {
        let predicate = NSPredicate(format: "SELF MATCHES %@", type(of:self).__emailRegex)
        return predicate.evaluate(with: self)
    }
}

ดังนั้นการใช้งานจึงเป็นเรื่องง่าย:

let str = "mail@domain.com"
if str.isEmail {
    print("\(str) is a valid e-mail address")
} else {
    print("\(str) is not a valid e-mail address")
}

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


2

สร้างส่วนขยายอย่างง่าย:

extension NSRegularExpression {

    convenience init(pattern: String) {
        try! self.init(pattern: pattern, options: [])
    }
}

extension String {

    var isValidEmail: Bool {
        return isMatching(expression: NSRegularExpression(pattern: "^[A-Z0-9a-z\\._%+-]+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}$"))
    }

    //MARK: - Private

    private func isMatching(expression: NSRegularExpression) -> Bool {
        return expression.numberOfMatches(in: self, range: NSRange(location: 0, length: characters.count)) > 0
    }
}

ตัวอย่าง:

"b@bb.pl".isValidEmail //true
"b@bb".isValidEmail //false

คุณสามารถขยายต่อไปนี้ขยายไปยังสิ่งที่คุณต้องการ: isValidPhoneNumber, isValidPasswordฯลฯ ...


โปรดทราบว่าNSRangeคุณสมบัติความยาวควรใช้String utf16.countแทนcharacters.count
Leo Dabus

2

ในSwift 4.2และ Xcode 10.1

//Email validation
func isValidEmail(email: String) -> Bool {
    let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
    var valid = NSPredicate(format: "SELF MATCHES %@", emailRegex).evaluate(with: email)
    if valid {
        valid = !email.contains("Invalid email id")
    }
    return valid
}

//Use like this....
let emailTrimmedString = emailTF.text?.trimmingCharacters(in: .whitespaces)
if isValidEmail(email: emailTrimmedString!) == false {
   SharedClass.sharedInstance.alert(view: self, title: "", message: "Please enter valid email")
}

หากคุณต้องการใช้ SharedClass

//This is SharedClass
import UIKit
class SharedClass: NSObject {

static let sharedInstance = SharedClass()

//Email validation
func isValidEmail(email: String) -> Bool {
    let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
    var valid = NSPredicate(format: "SELF MATCHES %@", emailRegex).evaluate(with: email)
    if valid {
        valid = !email.contains("Invalid email id")
    }
    return valid
}

private override init() {

}
}

และฟังก์ชั่นการโทรแบบนี้ ....

if SharedClass.sharedInstance. isValidEmail(email: emailTrimmedString!) == false {
   SharedClass.sharedInstance.alert(view: self, title: "", message: "Please enter correct email")
   //Your code here
} else {
   //Code here
}

1

ฉันสร้างไลบรารีที่ออกแบบมาสำหรับการตรวจสอบความถูกต้องของอินพุตและหนึ่งใน "โมดูล" ช่วยให้คุณสามารถตรวจสอบสิ่งต่าง ๆ ได้อย่างง่ายดาย ...

ตัวอย่างเช่นการตรวจสอบอีเมล:

let emailTrial = Trial.Email
let trial = emailTrial.trial()

if(trial(evidence: "test@test.com")) {
   //email is valid
}

SwiftCopเป็นห้องสมุด ... หวังว่ามันจะช่วยได้!


1

นี่คือส่วนขยายใน Swift 3

extension String {
    func isValidEmail() -> Bool {
        let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        return NSPredicate(format: "SELF MATCHES %@", emailRegex).evaluate(with: self)
    }
}

เพียงใช้มันเช่นนี้

if yourEmailString.isValidEmail() {
    //code for valid email address
} else {
    //code for not valid email address
}

การเปลี่ยนมาใช้ Regex จากคำตอบของ alexcristea เป็นโซลูชั่นที่สมบูรณ์แบบ
ittgung

0

เนื่องจากมีชื่อโดเมนระดับบนสุดแปลกมากมายตอนนี้ฉันหยุดตรวจสอบความยาวของโดเมนด้านบน ...

นี่คือสิ่งที่ฉันใช้:

extension String {

    func isEmail() -> Bool {
        let emailRegEx = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"
        return NSPredicate(format:"SELF MATCHES %@", emailRegEx).evaluateWithObject(self)
    } 
}

0

ดูเหมือนว่าจะทำงานด้วย ...

let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"

func validate(email: String) -> Bool {
    let matches = email.rangeOfString(regex, options: .RegularExpressionSearch)
    if let _ = matches {
        return true
    }
    return false
}

0

อัปเดตคำตอบ @Arsonik เพื่อ Swift 2.2 โดยใช้โค้ด verbose น้อยกว่าโซลูชันที่เสนออื่น ๆ :

extension String {
    func isValidEmail() -> Bool {
        let regex = try? NSRegularExpression(pattern: "^[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])?)*$", options: .CaseInsensitive)
        return regex?.firstMatchInString(self, options: [], range: NSMakeRange(0, self.characters.count)) != nil
    }
}

abcd @ a กำลังผ่าน regex นี้ คุณควรแก้ไข
Gunhan

โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

0

คำตอบของ @ JeffersonBe อยู่ใกล้ แต่ส่งคืนtrueถ้าสตริงเป็น "สิ่งที่มี someone@something.com อีเมลที่ถูกต้อง" ซึ่งไม่ใช่สิ่งที่เราต้องการ ต่อไปนี้เป็นส่วนขยายของ String ที่ใช้งานได้ดี (และอนุญาตให้ทดสอบหมายเลขโทรศัพท์ที่ถูกต้องและเครื่องตรวจจับข้อมูลอื่น ๆ เพื่อเริ่มระบบ

/// Helper for various data detector matches.
/// Returns `true` iff the `String` matches the data detector type for the complete string.
func matchesDataDetector(type: NSTextCheckingResult.CheckingType, scheme: String? = nil) -> Bool {
    let dataDetector = try? NSDataDetector(types: type.rawValue)
    guard let firstMatch = dataDetector?.firstMatch(in: self, options: NSRegularExpression.MatchingOptions.reportCompletion, range: NSRange(location: 0, length: length)) else {
        return false
    }
    return firstMatch.range.location != NSNotFound
        // make sure the entire string is an email, not just contains an email
        && firstMatch.range.location == 0
        && firstMatch.range.length == length
        // make sure the link type matches if link scheme
        && (type != .link || scheme == nil || firstMatch.url?.scheme == scheme)
}
/// `true` iff the `String` is an email address in the proper form.
var isEmail: Bool {
    return matchesDataDetector(type: .link, scheme: "mailto")
}
/// `true` iff the `String` is a phone number in the proper form.
var isPhoneNumber: Bool {
    return matchesDataDetector(type: .phoneNumber)
}
/// number of characters in the `String` (required for above).
var length: Int {
    return self.characters.count
}

โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

0

และสำหรับSwift 3 :

extension String {
    func isValidEmail() -> Bool {
        let regex = try? NSRegularExpression(pattern: "^[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])?)*$", options: .caseInsensitive)
        return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.characters.count)) != nil
    }
}

โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

0

สิ่งเดียวที่ฉันมีในรายการคำตอบคือสำหรับ Linux NSRegularExpressionไม่มีอยู่จริง ๆRegularExpression

    func isEmail() -> Bool {

    let patternNormal = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"

    #if os(Linux)
        let regex = try? RegularExpression(pattern: patternNormal, options: .caseInsensitive)
    #else
        let regex = try? NSRegularExpression(pattern: patternNormal, options: .caseInsensitive)
    #endif

    return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.characters.count)) != nil

การรวบรวมนี้ประสบความสำเร็จทั้งใน macOS และ Ubuntu


โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus

0

ทางออกที่ดีที่สุดด้วยผลลัพธ์ที่ดีที่สุดสำหรับ

Swift 4.x

 extension String {

        func validateAsEmail() -> Bool {
            let emailRegEx = "(?:[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])+)\\])"

            let emailTest = NSPredicate(format:"SELF MATCHES[c] %@", emailRegEx)
            return emailTest.evaluate(with: self)
        }
    }

0

ฉันชอบที่จะสร้างส่วนขยาย

   extension String {

func isValidateEmail() -> Bool {
    let emailFormat = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
    let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailFormat)
    return emailPredicate.evaluate(with: self)
}

}

การใช้งาน:

if emailid.text!.isValidateEmail() == false(){
 //do what ever you want if string is not matched.

}

0

สวิฟท์ 5

 func isValidEmailAddress(emailAddressString: String) -> Bool {

 var returnValue = true
 let emailRegEx = "[A-Z0-9a-z.-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,3}"

 do {
        let regex = try NSRegularExpression(pattern: emailRegEx)
        let nsString = emailAddressString as NSString
        let results = regex.matches(in: emailAddressString, range: NSRange(location: 0, length: nsString.length))

        if results.count == 0
        {
            returnValue = false
        }

    } catch let error as NSError {
        print("invalid regex: \(error.localizedDescription)")
        returnValue = false
    }

    return  returnValue
}

แล้ว:

let validEmail = isValidEmailAddress(emailAddressString: "your@email.com")
print(validEmail)

0

Perfect Regex เช่น Google Email

"^[A-Z0-9a-z][a-zA-Z0-9_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"

2
ใครก็ตามที่โหวตคำตอบของฉันโปรดตรวจสอบความรู้ของคุณ ฉันได้ใช้ regex นี้ในรหัสจำนวนมากและเพื่อนของฉันใช้ regex นี้และมันใช้งานได้ดีมาก ..
ami rt

ฉันคิดว่าฉันสามารถตอบ: regex ของคุณง่ายและไม่ตรงกับ RFC ตัวอย่างเช่นอีเมลสามารถมีคำพูดและแม้กระทั่งช่องว่างในส่วนแรก! ดูhaacked.com/archive/2007/08/21/…
Hugal31 31

1
ขออภัยพี่ชายฉันคิดว่าคุณควรตรวจสอบการตรวจสอบอีเมลของ Google ไม่มีวิธีเพิ่ม Space ในส่วนแรกของอีเมลและถ้า regex ของฉันผิดแล้วทำไมไม่มีใครโพสต์เขียนและ regex ที่สมบูรณ์แบบ
ami rt

ตาม RFC 5322 "Hello world!" @ example.com เป็นอีเมลที่ถูกต้อง อันที่จริงมันเป็นไปไม่ได้ที่จะสร้าง regex ที่ถูกต้อง ผู้ให้บริการอีเมลทุกรายจะใช้การตรวจสอบอีเมลของ Google
Hugal31

1
นั่นคือสิ่งที่ฉันต้องการฟังและนั่นคือเหตุผลที่ฉันพูดในหัวข้อตัวหนาที่เหนือ regex เป็นเหมือน Google ขอบคุณ
ami rt

-1

หรือคุณสามารถมีส่วนขยายสำหรับข้อความเสริมของ UITextField:

วิธีใช้:

if  emailTextField.text.isEmailValid() {
      print("email is valid")
}else{
      print("wrong email address")
}

ส่วนขยาย:

extension Optional where Wrapped == String {
    func isEmailValid() -> Bool{
        guard let email = self else { return false }
        let emailPattern = "[A-Za-z-0-9.-_]+@[A-Za-z0-9]+\\.[A-Za-z]{2,3}"
        do{
            let regex = try NSRegularExpression(pattern: emailPattern, options: .caseInsensitive)
            let foundPatters = regex.numberOfMatches(in: email, options: .anchored, range: NSRange(location: 0, length: email.count))
            if foundPatters > 0 {
                return true
            }
        }catch{
            //error
        }
        return false
    }
}

โปรดทราบว่าคุณสมบัติความยาว NSRange ควรใช้ String utf16.count แทน characters.count
Leo Dabus
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.