【问题标题】:Regular expression for parsing name and email from text [duplicate]用于从文本中解析名称和电子邮件的正则表达式 [重复]
【发布时间】:2016-02-23 20:32:21
【问题描述】:

这是我目前形成的正则表达式:

/(?:("?(?:.*)"?)\s*)?\s<(.*@.*)>|(?:mailto:(.*@.*))|(.*@.*)/gi

You can check it out at regex101

我正在尝试从以下内容中提取“姓名”和“电子邮件”:

John Smith <john.smith@gmail.com>
John Smith <johnsmith@gmail.com>
"John Smith" <johnsmith@gmail.com>
"John" <johnsmith@gmail.com>
John Smith<johnsmith@gmail.com>
<johnsmith@gmail.com>
johnsmith@gmail.com
mailto:johnsmith@gmail.com
"John"<johnsmith@gmail.com>

To: John Smith <john.smith@gmail.com>
From: John Smith <john.smith@gmail.com>
Reply-to: john.smith@gmail.com
Return-path: <john.smith@gmail.com>
Message-id: <john.smith@gmail.com>
References: <john.smith@gmail.com>
Original-recipient: rfc822;john.smith@gmail.com
for john.smith@gmail.com
ESMTPSA id <john.smith@gmail.com>
domain of john.smith@gmail.com
envelope-from=john.smith@gmail.com
(ORCPT john.smith@gmail.com)

从头开始,我感觉自己好像几乎在那里 - 但在 3 件事上遇到了麻烦:

  • 从第一个捕获组中去除双引号

  • 处理缺少空格的变体:John Smith&lt;johnsmith@gmail.com&gt;

  • 后一个块的“名称”字段中的误报,所以我需要一种排除这些的方法(可能使用前面的::=for、@987654329 @,of?)

作为一个完整的正则表达式新手,我希望有人能提供一些关于我如何克服这些问题的指导。

出于好奇,我很遗憾丢失了我的 CardDAV 以及所有联系人,所以以真正的 Linux 方式,我将通过手动解析我的整个原始 MBOX 来重建一个电子邮件列表,按最常见的排序,然后去从那里开始。

我将使用 bash grep 或 perl sed

感谢您的宝贵时间!

【问题讨论】:

  • bash 和 javascript?
  • 如果可能的话,我想像现在一样使用 bash/grep。但据我了解,正则表达式存在复杂性限制,因此如果需要,我很乐意使用 javascript 或 perl。
  • JS 正则表达式比 bash 更受限制。而bash提供了sed,它使用了Perl的正则表达式。
  • 我没有意识到这一点,我读到相反的情况。我知道使用 GNU grep,因为它可以使用 perl 正则表达式,但我没有想到 sed。无论哪种方式,我都更喜欢 bash,所以使用 grepsed 是完美的。
  • 从您的示例中,envelope-from=john.smith@gmail.com 可能也是一个有效的电子邮件地址;没有上下文,就无法判断。

标签: regex linux bash perl sed


【解决方案1】:

这是另一种可能的正则表达式,为了清楚起见,我将其分成三行,但应该在一行:

\s*(?:.*?[:=;]|ORCPT|for|domain of|ESMTPSA id)?
\s*(?:"?([\w ]*?)[ "<])?
\s*<?([\w.]*?@[\w.]*)>?

第一行消除了前缀,因此是非捕获的。它消除了以:;= 或某些特定文字结尾的任何内容。

第二行和第三行分别是name和email的两个捕获组。

它正确解析了您提供的示例。

regex fiddle

Perl 解决方案

你可以启动这个perl onliner:

perl -ne 'while(/.../gi){print "$1|$2\n";}' yourinputfile

这将输出捕获的组 1 和 2,由管道字符分隔:

John Smith|john.smith@gmail.com
John Smith|johnsmith@gmail.com
John Smith|johnsmith@gmail.com
John|johnsmith@gmail.com
John Smith|johnsmith@gmail.com
|johnsmith@gmail.com
|johnsmith@gmail.com
|johnsmith@gmail.com
John|johnsmith@gmail.com
John Smith|john.smith@gmail.com
John Smith|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com
|john.smith@gmail.com

【讨论】:

  • 出色的工作,trincot。使用非捕获组是完美的 - 但名称 "John Smith " 中有一个尾随空格。我该如何纠正这个问题,同时仍然尊重名字和姓氏之间的空格?我玩过([\w ]*)[ "&lt;])?\s* 无济于事。非常感谢。
  • 通过使“名称”捕获组非贪婪来修复该问题(将 [\w ]* 更改为 [\w ]*?)。我还使前缀匹配不捕获,并相应地更新了正则表达式小提琴。
  • 你有我永远的感谢 trincot!另外,回答接受。 :) 最后,正则表达式似乎与grep 不兼容,我尝试过双引号和单引号。我想正则表达式对于grep 来说太高级了——你能推荐另一个 cli 工具吗,最好已经捆绑在 vanilla linux 中? $ grep -E o -h '/\s*(?:.*?[:=;]|ORCPT|for|domain of|ESMTPSA id)?\s*(?:"?([\w ]*?)[ "&lt;])?\s*&lt;?([\w.]*?@[\w.]*)&gt;?/gi' examples
  • 我假设问题是未转义字符?我必须全部逃脱吗?
  • 使用grep 的替代品似乎更有意义。我读过关于 grep -P 的文章,但它似乎对我在 Linux Mint 上也不起作用。
【解决方案2】:

只是一个建议。可能对您来说更适合检查“电子邮件之前”和“电子邮件”以及从程序逻辑中提取处理“电子邮件之前”之后。像这样:

((?:(?![a-z.]+@[a-z.]+\.[a-z]{2,4})(?:.|\r))+)([a-z.]+@[a-z.]+\.[a-z]{2,4})

((?:(?!regex)(?:.|\r))+)(regex) - 这意味着“与正则表达式不匹配的东西”,但这是每个符号,包括从一到无限次重复的字符返回,并将其记住到第一个反向引用中,然后尝试匹配正则表达式并将其放入在第二个反向引用中。

编辑: 如果您想处理第一组不存在的情况(仅存在电子邮件),这里是修改版本。

((?:(?![a-z.]+@[a-z.]+\.[a-z]{2,4})(?:.|\r))*)([a-z.]+@[a-z.]+\.[a-z]{2,4})

* instead + 

编辑2: 根据 trincot 的评论进行改进。

((?:(?![^><@\s=;]+@[^><@\s=;]+\.[a-z]{2,4})(?:.|\r))*)([^><@\s=;]+@[^><@\s=;]+\.[a-z]{2,4})

【讨论】:

  • 谢谢你,我很欣赏这个逻辑,我没想到会那样做。它唯一没有选择的是“johnsmith@gmail.com”变体。您可以在regex101 上看到它。虽然用程序逻辑处理“之前”是可能的,但如果我可以将它压缩到一个全能的正则表达式中,它会让事情变得更简单。我有很多年的电子邮件要解析,这是很多误报...
  • 我也编辑了处理这种情况的答案。
  • 电子邮件地址可以不只是 [a-z.]
  • 是的。请记住。
  • 许多有效的当前顶级域的字符多于四个字符,其中许多字符来自比过于严格的[a-z] 更广泛的集合。
猜你喜欢
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
  • 2012-09-30
  • 2010-11-03
  • 1970-01-01
  • 2010-11-18
  • 1970-01-01
相关资源
最近更新 更多