如何在 swift 中验证电子邮件地址?

How to validate an e-mail address in swift?

提问人:giorgionocera 提问时间:8/24/2014 最后编辑:Nareshgiorgionocera 更新时间:6/19/2023 访问量:283440

问:

有谁知道如何在 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;
    }
}

但我无法将其翻译成 Swift。

iOS Swift 验证 电子邮件 NSCorgentExpression

评论

8赞 Sulthan 8/24/2014
翻译应该简单明了。哪个部分给你带来了问题?
17赞 Matthias Bauch 2/8/2015
别忘了祈祷你的用户都没有一个新的顶级域名。例如.coffee
3赞 mouviciel 9/18/2017
正则表达式不适用于验证用户是否输入了其电子邮件地址。唯一 100% 正确的方法是发送激活电子邮件。请参阅:在阅读 RFC 之前,我知道如何验证电子邮件地址
5赞 Fattie 2/18/2019
这是一个引人入胜的QA。这几乎可以肯定是整个网站上“最错误”的QA。目前 #1 的 600 票答案(什么?!) 在各种可能的方式上都是绝对的、完全错误的(每一行都是完全错误的,每个概念和想法都是错误的........ !!!)许多其他高票的答案要么是“完全错误的”,要么是“非常笨拙的”,要么是完全破碎的,甚至没有编译。此外,虽然这个 Q 的本质要求“精英正则表达式工程”,但许多答案(高票!)都具有骇人听闻的正则表达式工程。这是一个非常有趣的QA!为什么??
3赞 David H 1/2/2020
我同意 Fattie 所说的一切——“足够好”和“近乎完美”之间的区别:github.com/dhoerl/EmailAddressFinder。也就是说,我编写了一个构建正则表达式的 Mac 项目,其中构建中的每个步骤都引用了相应的 RFC。它正确地处理了几个“边缘情况”测试套件,旨在捕获不正确的正则表达式。但是,正如他所说,它将允许“x@x”,因为该地址符合规范。还有一个 GitHub 项目(找不到链接),它提供了一长串电子邮件服务器,并且可能覆盖了您需要测试的 99% 的服务器。

答:

893赞 Maxim Shoustin 8/24/2014 #1

我会使用 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)
}

对于 3.0 之前的 Swift 版本:

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)
}

对于 1.2 之前的 Swift 版本:

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
}

评论

7赞 Sulthan 8/24/2014
不会更简单易读吗?相比之下有点像 Javascript。return emailTest.evaluateWithObject(testStr)== true
20赞 CularBytes 7/13/2015
它不会检查是否有可用的扩展程序,a@a已经可以:(
8赞 Alan 10/28/2016
这不适用于 [email protected]
3赞 Ben Sullivan 11/23/2016
这不会检测到 [email protected] 或 [email protected]。下面@alexcristea的答案确实如此
7赞 Fattie 5/1/2019
有趣的是,............以及 (1) 正则表达式完全不正确 (2) 正则表达式(即使在它试图做的事情的上下文中)有重大错误 (3) Swift 是错误的 (4) 即使把它放在一边,风格也是完全错误的 (5) 考虑到所有其他情况,这并不重要,但它甚至没有提到你必须缓存谓词......幽默的是,(6)仍然有剩余的代码(“日历”——什么?)来自它从哪里复制的。
9赞 zaph 8/24/2014 #2

这是一个基于以下方法的方法: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)
    return range != nil
}

注意:更新了 TLD 长度。

这是根据 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

请注意,没有 Objective-C 或 Swift 等语言所要求的转义。

评论

2赞 Antzi 6/15/2015
您使用的 emailRegEx 是完全错误的。它只允许 2 到 4 个字符长的 TLD,而像这样的域存在。.engineer
0赞 zaph 6/15/2015
明白了,我不是在为我的答案辩护,而是在为编辑的水平辩护。如上所述添加评论,投反对票,指向更好的答案,添加您自己的答案。不宜对答案进行实质性的更改。为了完整起见,我添加了扩散正则表达式。
0赞 Fattie 10/15/2019
为什么哦,为什么不直接删除答案呢?有什么可能的理由把它留在这里?
0赞 mojuba 5/8/2022
哦,上帝,这里的很多答案都是错误的。正则表达式应括在 ^...$ 中,否则它将与输入字符串中的任何位置的正则表达式匹配!
120赞 Arsonik 10/22/2014 #3

作为类扩展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赞 Zack Shapiro 7/29/2015
countElements是现在count
30赞 Cullen SUN 9/29/2015
xxx@yyy返回 true?
1赞 Rémy Virin 10/16/2015
与 Cullen SUN 相同,foo@bar返回 true。
4赞 Wed 2/9/2018
没有 .tld user@host也是一个有效的电子邮件地址,例如root@localhost
1赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
15赞 Joel García Verástica 9/30/2015 #4

这是 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赞 Rémy Virin 10/16/2015
foo@bar返回真实?!
2赞 netshark1000 3/7/2016
验证aa@aach为 true
5赞 Dulgan 8/4/2016
这是因为 RFC 验证这些电子邮件地址是否符合真实;)
0赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
0赞 Fattie 5/16/2018
不缓存谓词真的是错误的/可怜的。这是苹果在doco中关于这个问题的第一件事。页面上的大多数答案都犯了一个明显的错误。
32赞 Nicolas Manzini 11/4/2015 #5

以下是两个最受好评的答案的融合,具有一个基本正确的正则表达式:一个使用谓词的 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)
        }
    }

评论

1赞 Ezequiel Adrian 8/6/2023
.evaluateWithObject(self) 已更改为 .evaluate(with: self)
1赞 Andres 11/5/2015 #6

我制作了一个专为输入验证而设计的库,其中一个“模块”允许您轻松验证一堆东西......

例如,要验证电子邮件:

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

if(trial(evidence: "[email protected]")) {
   //email is valid
}

SwiftCop 是图书馆...希望对您有所帮助!

5赞 lee5783 1/30/2016 #7

对于 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赞 AZOM 3/30/2016
这对我来说似乎很好用。据我了解,您甚至可以省略“A-Z”(大写字母),因为您可以选择.反正不区分大小写设置...
0赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
0赞 snowmen10 2/26/2016 #8

既然现在有这么多奇怪的顶级域名,我就不再检查顶级域名的长度了......

这是我使用的:

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)
    } 
}
9赞 Andrea.Ferrando 2/26/2016 #9

这里有很多正确的答案,但许多“正则表达式”是不完整的,可能会发生像“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
        }
    }
}

评论

0赞 Juan Boero 3/15/2016
无法正常工作,它允许您在域后添加空格。
0赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
1赞 Andrea.Ferrando 2/21/2019
@Fattie争论你的陈述。您的评论非常无用,建议改进,提出修复建议。说完全错误是非常愚蠢的,并且是封闭心态的基础
0赞 Fattie 3/15/2021
它立即完全无法通过最琐碎和最明显的测试。
120赞 Nazik 3/4/2016 #10

编辑,针对 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赞 netshark1000 3/7/2016
第一个具有有效正则表达式。其他人验证aa@aach为真
1赞 Nazik 3/8/2016
@netshark1000,只有投赞成票,任何答案都会名列前茅。:)
0赞 Guillaume Laurent 8/8/2018
NSRegularExpression 比 NSPredicate 更易于使用
2赞 Prashant Gaikwad 11/23/2018
它不处理域名后面的两个点条件。试试这个答案 stackoverflow.com/a/53441176/5032981
0赞 Neeraj Joshi 11/20/2019
@AzikAbdullah 如果您输入“[email protected]”,那么它也将进行验证
24赞 JeffersonBe 6/21/2016 #11

我建议将其用作 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 "[email protected]".isEmail { // true
    print("Hold the Door")
}

评论

2赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
1赞 Duan Nguyen 4/12/2018
更新 Swift 4: 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: count)) return (firstMatch?.range.location != NSNotFound && firstMatch?。网址?。方案 == “mailto”) }
0赞 akashlal.com 6/15/2021
绝对给力!虽然其他答案解决了英文字母电子邮件的验证,但这个野兽也验证了阿拉伯语和其他语言的电子邮件。干的好!
0赞 Jano 3/1/2022
这很容易被愚弄:“mailto://www.google.com”.isEmail == true,但它不是电子邮件。
1赞 Matias Seijas 8/10/2016 #12

更新了 Swift 2.2 的答案@Arsonik答案,使用比其他解决方案更详细的代码:

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
    }
}

评论

0赞 Gunhan 2/16/2017
abcd@a正在通过这个正则表达式。你应该修复它。
0赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
81赞 alexcristea 9/18/2016 #13

如果您正在寻找一种干净简单的解决方案来做到这一点,您应该看看 https://github.com/nsagora/validation-components

它包含一个电子邮件验证谓词,可以轻松集成到您的代码中:

let email = "[email protected]"
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])+)\\])"

评论

4赞 Samuel Ev 11/11/2016
哇,不知道 emailregex.com。太棒了!
4赞 Ben Sullivan 11/23/2016
最后,一个过滤 [email protected]
0赞 Anil Gupta 10/11/2018
它正在与精确 -- [email protected] 一起工作。它不是验证abc@abc
0赞 Ümañg ßürmån 2/27/2020
啊,终于..:D
0赞 Andy Weinstein 11/25/2020
这似乎允许一个字符的域后缀,这在技术上是合法的,但所有这些都由注册商持有,因此在实践中它始终是无效的。
2赞 Kudit 9/18/2016 #14

@JeffersonBe的答案很接近,但如果字符串是“包含 [email protected] 有效电子邮件的东西”,则返回,这不是我们想要的。以下是 String 上的一个扩展,它运行良好(并允许测试有效的 phoneNumber 和其他数据检测器以启动。true

/// 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
}

评论

0赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
0赞 Jano 3/1/2022
“mailto://www.google.com”.isEmail == true,但不是电子邮件。
1赞 Andrei Popa 10/25/2016 #15

我对回复列表的唯一补充是,对于 Linux,它不存在,它实际上是NSRegularExpressionRegularExpression

    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上都能成功编译。

评论

0赞 Leo Dabus 2/23/2018
请注意,NSRange 长度属性应使用字符串 utf16.count 而不是 characters.count
7赞 Marlon Ruiz 10/26/2016 #16

我更喜欢为此使用扩展。此外,此 url http://emailregex.com 可以帮助您测试正则表达式是否正确。事实上,该网站为某些编程语言提供了不同的实现。我分享了我对 Swift 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)
    }
}

评论

0赞 Fattie 1/22/2017
有几个问题..例如,您可以拥有 ..[email protected] 那里有一个奇怪的点
74赞 Fattie 1/22/2017 #17

(序言。请注意,在某些情况下,您现在可以使用此 iOS 内置的解决方案:https://multithreaded.stitchfix.com/blog/2016/11/02/email-validation-swift/ )


唯一的解决方案:

1 - 它避免了示例代码中经常出现的可怕的正则表达式错误

2 - 它不允许荒谬的电子邮件,例如“x@x”

(如果出于某种原因,您需要一个允许无意义字符串(如“x@x”)的解决方案,请使用其他解决方案。

3 - 代码非常容易理解

4 - 它是 KISS,可靠,并在拥有大量用户的商业应用程序上经过测试以破坏

5 - 谓词是全局的,正如 Apple 所说的那样

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() ?? false
    }
}

就这么简单。

对正则表达式新手的解释:

在此描述中,“OC”表示普通字符 - 字母或数字。

__firstpart......必须以 OC 开头和结尾。对于中间的字符,您可以使用某些字符,例如下划线,但开头和结尾必须是 OC。 (但是,只有一个 OC 是可以的,仅此而已,例如:[email protected])

__serverpart......你有像“废话”这样的部分,它们重复。(例如,mail.city.fcu.edu。这些部分必须以 OC 开头和结尾,但在中间也可以有一个破折号“-”。(例如,w.campus.edu)您最多可以有五个部分,您必须有一个最后,TLD(如 .com)的大小严格为 2 到 8。(显然,只需根据支持部门的首选更改“8”即可。


重要!

您必须将谓词保留为全局,不要每次都构建它。

请注意,这是Apple在文档中提到的关于整个问题的第一件事

不缓存谓词的建议是非启动器。


非英文字母

当然,如果您处理非英语字母,请进行适当的调整。

评论

2赞 thetrutz 7/31/2018
关于第(4)点:你是如何与很多用户一起测试的?您是否跟踪了无法注册商业应用程序的用户,因为正则表达式确实阻止了他们使用他们的电子邮件地址?唯一“合理”的应该是规范 (RFC) 指定的内容,或者如果无法实现,则更轻松,但涵盖规范中的所有内容。如果不允许用户输入x@x,他们将输入一些 [email protected],这将传递您/任何正则表达式。
2赞 humblePilgrim 9/11/2020
这是一个很棒的答案。我试图在用户键入时验证电子邮件 ID 并切换按钮的启用状态。对于其他正则表达式,当我键入 aaa@aaa 时,该按钮会被启用,然后在添加“.”时被禁用。这在我的情况下按预期工作。
4赞 Lucas Tegliabue 12/3/2020
嗨,@Fattie,您能否为我们提供一个链接,其中 Apple 解释说谓词必须是全局的?谢谢
2赞 andbi 6/25/2021
@Fattie例如“user@домен.рф”。虽然绝对有效,但它没有通过验证。
2赞 Fattie 8/5/2021
@andbi - 这是一个非常合理的观点。给出的例子(对不起)仅适用于英语字母表。你肯定是对的,在许多情况下,程序员会添加外来字母。这是最重要的一点,谢谢你提到它。
2赞 Bartłomiej Semańczyk 5/10/2017 #18

创建简单扩展:

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
    }
}

例:

"[email protected]".isValidEmail //true
"b@bb".isValidEmail //false

您可以将以下扩展扩展到您需要的任何内容:等等...isValidPhoneNumberisValidPassword

评论

0赞 Leo Dabus 2/23/2018
请注意,length 属性应使用 而不是NSRangeStringutf16.countcharacters.count
1赞 Gefilte Fish 11/22/2017 #19

这是 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
}

评论

0赞 ittgung 11/30/2017
更改为使用 alexcristea' 答案中的正则表达式,这是完美的解决方案。
1赞 Abdelahad Darwish 5/11/2018 #20

最佳解决方案,最佳结果

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)
        }
    }
6赞 Alejandro Iván 5/16/2018 #21

这是 @Fattie 的“THE REASONABLE SOLUTION”的新版本,在 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 = "[email protected]"
if str.isEmail {
    print("\(str) is a valid e-mail address")
} else {
    print("\(str) is not a valid e-mail address")
}

我只是不喜欢在对象上添加一个,因为电子邮件地址是它们固有的(或不是)。因此,根据我的理解,属性比属性更适合。funcStringBoolfunc

评论

0赞 Lucas Tegliabue 12/3/2020
但在这里,NSPredicate 不再是全球性的。
0赞 Alejandro Iván 12/4/2020
@LucasTegliabue这是一个两年多前的答案,但现在可能是错误的。
0赞 Lucas Tegliabue 12/26/2020
不会改变以下事实:在代码段中,NSPredicate 不是全局的。NSPredicate 具有与 String 实例相同的生命周期,所以根本不是全局的,或者我丢失了什么?
1赞 JavaBeast 6/3/2018 #22

我改进了@Azik答案。我允许使用指南允许的更多特殊字符,并返回一些额外的边缘情况作为无效。

该小组认为,根据指导方针,这里只允许本地部分是不正确的。请参阅@Anton Gogolev 对这个问题的回答或见下文:._%+-

电子邮件地址的本地部分可以使用以下任何 ASCII 字符:

  • 大写和小写拉丁字母 to 和 toAZaz;

  • digits 设置为09;

  • 特殊字符!#$%&'*+-/=?^_`{|}~;

  • dot ,前提是除非引用,否则它不是第一个或最后一个字符,并且除非引用,否则它不会连续出现(例如 是不允许的,但 允许);.[email protected]"John..Doe"@example.com

  • 允许使用空格和字符 限制(它们只允许在带引号的字符串中,如 在下面的段落中描述,此外,反斜杠或 双引号前面必须有反斜杠); 允许评论"(),:;<>@[\]

  • 在局部部分的两端都有括号;例如: 并且都等价于john.smith(comment)@example.com(comment)[email protected][email protected];

我使用的代码不允许受限制的不合适的特殊字符,但会允许比这里的大多数答案更多的选项。为了谨慎起见,我宁愿更宽松的验证,也不愿犯错误。

if enteredText.contains("..") || enteredText.contains("@@") 
   || enteredText.hasPrefix(".") || enteredText.hasSuffix(".con"){
       return false
}

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: enteredText)

评论

0赞 The iOSDev 12/9/2020
感谢第一部分包含检查和返回 false
4赞 ikbal 9/12/2018 #23

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 
    {

    }
1赞 Naresh 10/12/2018 #24

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
}
39赞 Ken Mueller 6/16/2019 #25

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)
    }
}

"[email protected]".isValidEmail

返回。。。

true

评论

12赞 rommex 3/10/2020
重复重复的答案有什么意义?它不依赖于任何 Swift 5 功能
1赞 Fero 11/17/2021 #26

下面是一个最新的 playground 兼容版本,它使用标准库,因此您不必维护正则表达式:

import Foundation

func isValid(email: String) -> Bool {
  do {
    let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
    let range = NSRange(location: 0, length: email.count)
    let matches = detector.matches(in: email, options: .anchored, range: range)
    guard matches.count == 1 else { return false }
    return matches[0].url?.scheme == "mailto"
  } catch {
    return false
  }
}

extension String {
  var isValidEmail: Bool {
    isValid(email: self)
  }
}

let email = "[email protected]"
isValid(email: email) // prints 'true'
email.isValidEmail // prints 'true'

评论

0赞 Jano 3/1/2022
NSDataDetector 解决方案不可靠,因为它们验证以下字符串:,这不是电子邮件。mailto://www.google.com
4赞 Jano 3/1/2022 #27

对 a 进行简单的测试,然后发送确认电子邮件。@.

考虑一下:

  • 世界上有一半的人使用非 ASCII 字符。
  • 正则表达式缓慢而复杂。顺便说一句,至少检查 char/letter/Unicode 范围,而不是 az。
  • 您负担不起完全验证的费用,因为 RFC 规则和相应的正则表达式太复杂了。

我正在使用这个基本检查:

// similar to https://softwareengineering.stackexchange.com/a/78372/22077
import Foundation

/**
 Checks that
 - length is 254 or less (see https://stackoverflow.com/a/574698/412916)
 - there is a @ which is not the first character
 - there is a . after the @
 - there are at least 4 characters after the @
*/
func isValidEmail(email: String) -> Bool {
    guard email.count <= 254 else { 
        return false 
    }
    let pos = email.lastIndex(of: "@") ?? email.endIndex
    return (pos != email.startIndex)
        && ((email.lastIndex(of: ".") ?? email.startIndex) > pos) 
        && (email[pos...].count > 4)
}

print(isValidEmail(email: "アシッシュ@ビジネス.コム")) // true

请注意,

  • 它比 regex 和 NSDataDetector 快得多。

  • 它正确地将以下内容报告为有效:

Håkan.Söderström@malmö.se"
[email protected]"
试@例子.测试.مثال.آزمایشی"
[email protected]"
[email protected]
  • 它错误地将以下内容报告为无效 - 因为它们实际上有效,但可能是用户错误的产物:
a @ b
a@b

相关:

2赞 Hassaan Fayyaz 5/23/2022 #28

当电子邮件存在基本问题时,上述大多数正则表达式示例都无法捕获错误。例如

  1. [email protected]- 连续点
  2. [email protected]- 点后@
  3. [email protected]- 点前@
  4. [email protected]- 以点开头

这是我使用过的一个字符串扩展,它使用具有更严格规则的正则表达式。

extension String {
    func isValidEmail() -> Bool {
        let emailRegEx = "^(?!\\.)([A-Z0-9a-z_%+-]?[\\.]?[A-Z0-9a-z_%+-])+@[A-Za-z0-9-]{1,20}(\\.[A-Za-z0-9]{1,15}){0,10}\\.[A-Za-z]{2,20}$"
        let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
        return emailPred.evaluate(with: self)
   }
}

以下是我们如何为它编写测试用例。

XCTAssertFalse("[email protected]".isValidEmail())
XCTAssertTrue("[email protected]".isValidEmail())
1赞 SomuYadav 1/23/2023 #29

在 Swift 5.7 中,借助 Regex 类,我们可以以简单有效的方式验证电子邮件地址

private func isValidEmail(_ email: String) -> Bool {
      guard let emailRegex = try? Regex("[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}")  
      else { return false }
      return email.firstMatch(of: emailRegex) != nil
   }

此外,我们还可以使用属性包装器来使其更有效率:

@propertyWrapper
struct EmailPropertyWrapper {
  private var _value: String
  var wrappedValue: String {
    get { return isValidEmail(_value) ? _value : String() }
    set { _value = newValue }
  }
  
  init(email: String) {
    _value = email
  }

  private func isValidEmail(_ email: String) -> Bool {
      guard let emailRegex = try? Regex("[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}")  
      else { return false }
      return email.firstMatch(of: emailRegex) != nil
   }
}

struct User {
  var name: String
  @EmailPropertyWrapper var email: String
  
  func validateProperty() -> Bool {
    if name.isEmpty || email.isEmpty { return false }
    return true
  }
}

let user = User(name: "Sy", email: .init(email: "[email protected]"))

print(user.validateProperty())
4赞 Nathan Hosselton 3/30/2023 #30

Apple 自己的文档(在 Swift 5.7 中引入)现在提供了一个最小电子邮件验证的示例,并完成了命名组件捕获。RegexBuilder

(复制在这里供后代使用,因为他们有时会改变他们的例子)

let word = OneOrMore(.word)
let emailPattern = Regex {
    Capture {
        ZeroOrMore {
            word
            "."
        }
        word
    }
    "@"
    Capture {
        word
        OneOrMore {
            "."
            word
        }
    }
}

let text = "My email is [email protected]."
if let match = text.firstMatch(of: emailPattern) {
    let (wholeMatch, name, domain) = match.output
    // wholeMatch is "[email protected]"
    // name is "my.name"
    // domain is "example.com"
}

尽管这个问题已经有太多的答案,但我觉得这值得分享,因为它:

  1. 使用最新的 Swift API
  2. 不会使正则表达式逻辑过于复杂(电子邮件验证的陷阱)
  3. 直接来自苹果(几乎就像他们知道的那样)