Regexp recognition of email address hard?
我最近在某个地方读到,写一个regexp来匹配一个电子邮件地址,考虑到标准的所有变化和可能性,是非常困难的,而且比我们最初设想的要复杂得多。
有人能提供一些关于这是为什么的见解吗?
是否有任何已知和经验证的regexp可以完全做到这一点?
使用regexps匹配电子邮件地址有哪些好的选择?
对于正式的电子邮件规范,是的,从技术上讲,通过regex是不可能的,因为像注释(特别是如果您不首先删除空白的注释)和各种不同的格式(电子邮件地址并不总是[email protected])。您可以接近(使用一些庞大且不可理解的regex模式),但检查电子邮件的更好方法是进行非常熟悉的握手:
- 他们告诉你他们的电子邮件
- 通过电子邮件向他们发送一个带有guid的配置链接
当他们点击链接时,你知道:
- 电子邮件是正确的
- 它存在
- 他们拥有它
比盲目接受电子邮件地址要好得多。
有许多Perl模块(例如)可以做到这一点。不要试图编写自己的regexp来完成它。看看
网址:https://metacpan.org/pod/mail::vrfy
https://metacpan.org/pod/rfc::rfc822::地址
网址:http://ex parrot.com/~pdw/mail-rfc822-address.html
其他语言也有类似的工具。下面是疯狂的regexp…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | (?:(?: )?[ \t])*(?:(?:(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t] )+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:( ?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*))*@(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\0 31]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\ ](?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+ (?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?: (?: )?[ \t])*))*|(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z |(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: ) ?[ \t])*)*\<(?:(?: )?[ \t])*(?:@(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?:\ r )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: ) ?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t] )*))*(?:,@(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])* )(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t] )+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*))*) *:(?:(?: )?[ \t])*)?(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+ |\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t ]))*"(?:(?: )?[ \t])*))*@(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031 ]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\]( ?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(? :(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(? : )?[ \t])*))*\>(?:(?: )?[ \t])*)|(?:[^()<>@,;:\".\[\] \000-\031]+(?:(? :(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )? [ \t]))*"(?:(?: )?[ \t])*)*:(?:(?: )?[ \t])*(?:(?:(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]| \\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<> @,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|" (?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*))*@(?:(?: )?[ \t] )*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\\ ".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(? :[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[ \]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*))*|(?:[^()<>@,;:\".\[\] \000- \031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|( ?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)*\<(?:(?: )?[ \t])*(?:@(?:[^()<>@,; :\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([ ^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\" .\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\ ] \\]|\\.)*\](?:(?: )?[ \t])*))*(?:,@(?:(?: )?[ \t])*(?:[^()<>@,;:\".\ [\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\]\ r\\]|\\.)*\](?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\] |\\.)*\](?:(?: )?[ \t])*))*)*:(?:(?: )?[ \t])*)?(?:[^()<>@,;:\".\[\] \0 00-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\ .|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@, ;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\]]))|"(? :[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*))*@(?:(?: )?[ \t])* (?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\". \[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[ ^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\] ]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*))*\>(?:(?: )?[ \t])*)(?:,\s*( ?:(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\\ ".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)(?:\.(?:( ?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[ \["()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t ])*))*@(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t ])+|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(? :\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+| \Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*))*|(?: [^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\".\[\ ]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)*\<(?:(?: ) ?[ \t])*(?:@(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\[" ()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(?:\.(?:(?: ) ?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<> @,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*))*(?:,@(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@, ;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t] )*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\\ ".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*))*)*:(?:(?: )?[ \t])*)? (?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\["()<>@,;:\". \[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t])*)(?:\.(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z|(?=[\[ "()<>@,;:\".\[\]]))|"(?:[^" \\]|\\.|(?:(?: )?[ \t]))*"(?:(?: )?[ \t]) *))*@(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t]) +|\Z|(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*)(?:\ .(?:(?: )?[ \t])*(?:[^()<>@,;:\".\[\] \000-\031]+(?:(?:(?: )?[ \t])+|\Z |(?=[\["()<>@,;:\".\[\]]))|\[([^\[\] \\]|\\.)*\](?:(?: )?[ \t])*))*\>(?:( ?: )?[ \t])*))*)?;\s*) |
无论如何,验证电子邮件地址并不是很有用。它不会捕获常见的拼写错误或虚构的电子邮件地址,因为这些地址在语法上看起来像有效的地址。
如果你想确认地址是有效的,你别无选择,只能发送一封确认邮件。
如果您只是想确保用户输入的内容看起来像电子邮件而不是"asdf",那么请检查@。更复杂的验证并不能真正提供任何好处。
(我知道这不能回答你的问题,但我认为无论如何都值得一提)
我现在已经整理了Cal Henderson、Dave Child、Phil Haack、Doug Lovell和RFC 3696的测试案例。总共158个测试地址。
我对我能找到的所有验证器运行了所有这些测试。对比如下:http://www.dominicsayers.com/isemail
当人们增强他们的验证器时,我将尝试使此页面保持最新。感谢Cal、Dave和Phil在编译这些测试时的帮助和合作以及对我自己的验证器的建设性批评。
人们应该特别注意RFC3696的勘误表。其中三个规范示例实际上是无效地址。地址的最大长度是254或256个字符,而不是320个字符。
这并不完全是胡说八道,因为允许像"+"这样的字符对于抵制垃圾邮件的用户非常有用,例如[email protected](即时一次性Gmail地址)。
只有当一个站点接受它时。
BNF中有一种上下文无关的语法,描述了RFC-2822中的有效电子邮件地址。它是复杂的。例如:
1 | " @"@example.com |
是有效的电子邮件地址。我不知道有什么regexp能完全做到这一点;给出的示例通常要求首先去掉注释。我编写了一个递归下降解析器来完全执行一次。
在我看来,是否接受奇怪的、不常见的电子邮件地址格式取决于人们想如何处理它们。
如果你在写邮件服务器,你必须非常准确,并且极其正确地接受你所接受的。因此,上面引用的"疯狂"regex是适当的。
不过,对于我们其他人来说,我们主要关心的是确保Web表单中的用户类型看起来合理,并且其中没有某种SQL注入或缓冲区溢出。
坦率地说,在注册邮件列表、时事通讯或网站时,是否有人真的关心让某人输入一个包含评论、换行、引号、空格、括号或其他乱七八糟的200个字符的电子邮件地址?对这些小丑的正确反应是"稍后当你有一个像[email protected]的地址时再来"。
我所做的验证包括确保只有一个"@";没有空格、空值或换行;"@"右边的部分至少有一个点(但不是一行中的两个点);没有引号、圆括号、逗号、冒号、感叹号、分号或反斜杠,所有这些都更有可能是尝试。在黑客比实际电子邮件地址的一部分。
是的,这意味着我拒绝了某些人可能试图在我的网站上注册的有效地址-也许我"错误地"拒绝了多达0.001%的真实地址!我可以忍受。
报价和各种其他很少使用但有效的RFC部分使其变得困难。除了"这很难"之外,我对这个话题还不太了解,无法做出明确的评论,但幸运的是,其他人已经详细地写了这篇文章。
对于有效的regex,perl mail::rfc822::address模块包含一个正则表达式,它显然可以工作,但前提是已经用空白替换了任何注释。(电子邮件地址中的评论?你知道为什么它比人们想象的要难…)
当然,其他地方大量使用的简化regex将验证几乎所有真正使用的电子邮件地址…
在Java中检查电子邮件附件的一个简单而好的方法是使用Apache Cuffon验证器库的EualValueAuthor。
在发送电子邮件之前,我总是在输入表单中对照类似的内容检查电子邮件地址——即使你只发现一些拼写错误。您可能不想为"传递失败"通知邮件编写自动扫描仪。-)
只需添加一个比@mmaibaum列出的正则表达式更不疯狂的正则表达式:
1 | ^[a-zA-Z]([.]?([a-zA-Z0-9_-]+)*)?@([a-zA-Z0-9\-_]+\.)+[a-zA-Z]{2,4}$ |
号
它不是防弹的,当然也不能覆盖整个电子邮件规范,但它确实能很好地覆盖最基本的需求。更好的是,它有点易于理解,可以编辑。
摘自世界级冷聚变资源houseoffusion.com的讨论。
有些风格的regex实际上可以匹配嵌套的括号(例如,与perl兼容的括号)。也就是说,我看到过一个regex,声称它与RFC822正确匹配,它是两页没有任何空白的文本。因此,检测有效电子邮件地址的最佳方法是向其发送电子邮件并查看其是否有效。
如果您在.NET框架上运行,只需尝试实例化一个
这真的很难,因为根据电子邮件规范RFC2822,在电子邮件地址中有很多东西是有效的。您通常看不到的内容,如+是电子邮件地址的完全有效字符。根据规范
在http://regexlib.com有一个完整的部分专门介绍电子邮件地址,这是一个很好的资源。我建议你确定什么标准对你来说很重要,然后找到一个匹配的标准。大多数人并不需要规范允许的所有可能性的完全支持。
试试这个:
1 | "(?:[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])+)\])" |
号
请在这里查看详细信息。
然而,与其实现RFC822标准,不如从另一个角度来看待它。如果邮件服务器不镜像标准,那么标准所说的内容并不重要。因此,我认为最好模仿最流行的邮件服务器在验证电子邮件地址时所做的操作。
许多人尝试过,许多人接近了。你可能想读维基百科的文章,还有其他一些。
具体来说,您需要记住的是,许多网站和电子邮件服务器都对电子邮件地址进行了宽松的验证,因此本质上它们并没有完全实现该标准。不过,这足以让电子邮件一直工作。
这个Java类中有一个验证器:http://www.leshazlewood.com/?P=23
这是由Shiro的创建者写的(正式的ki,正式的jsecurity)
电子邮件地址有效性测试的利弊:
有两种类型的regex用于验证电子邮件:
正则表达式不可能匹配所有有效的电子邮件地址,也不可能匹配任何无效的电子邮件地址,因为某些字符串可能看起来像有效的电子邮件地址,但实际上不会转到任何人的收件箱。测试电子邮件是否有效的唯一方法是将电子邮件发送到该地址,并查看您是否得到某种响应。考虑到这一点,对于匹配电子邮件过于严格的正则表达式似乎没有太大的用途。
我认为大多数要求电子邮件regex的人都在寻找第一个选择,regex太宽松了。他们想测试一个字符串,看看它是否像一封电子邮件,如果它绝对不是一封电子邮件,那么他们可以对用户说:"嘿,你应该在这里放一封电子邮件,而这绝对不是一封有效的电子邮件。也许你没有意识到这个字段是用于电子邮件,或者可能有一个打字错误"。
如果用户输入的字符串看起来很像有效的电子邮件,但实际上不是有效的,那么这是一个问题,应该由应用程序的其他部分处理。
为了保证本文的完整性,对于PHP也有一个语言内置函数来验证电子邮件。
对于php,使用nice filter_var和特定的电子邮件验证类型:)
在php:d中不再有疯狂的电子邮件正则表达式
1 | var_dump(filter_var('[email protected]', FILTER_VALIDATE_EMAIL)); |
http://www.php.net/filter_var
有人能提供一些关于这是为什么的见解吗?
是的,这是一个非常复杂的标准,允许许多东西,没有人真正使用今天。:)
是否有任何已知和经验证的regexp可以完全做到这一点?
这里有一个完全解析整个标准的尝试…
网址:http://ex parrot.com/~pdw/mail-rfc822-address.html
使用regexps匹配电子邮件地址有哪些好的选择?
我猜,你用的是什么语言来使用现有的框架?不过,它们可能会在内部使用regexp。它是一个复杂的字符串。regexps是为解析复杂字符串而设计的,因此这确实是您的最佳选择。
编辑:我应该补充一下,我链接到的regexp只是为了好玩。我不赞成使用这样复杂的regexp——有些人说,"如果您的regexp不止一行,那么它肯定在某个地方有一个bug"。我链接到它来说明这个标准有多复杂。
除了WaynesAnswer,还有一个专门针对电子邮件的www.regular-expressions.info部分,其中有一些示例。
您可以一直质疑它是否值得,或者实际上,覆盖regexp的小于100%的内容是否只会导致错误的安全感。
最后,实际发送电子邮件将提供真正的最终验证。(-您将发现邮件服务器是否有错误;-)