正则表达式从电子邮件正文中提取新内容

时间:2011-09-17 19:56:44

标签: regex

给定一个表示电子邮件整个文本正文的字符串,我想只提取发件人组成的部分,如果它只是一个连续的文本块。例如:

Dear Sir:
That is a good point.

On Wednesday, June 1, John wrote:
> Hello world.

将提取:

Dear Sir:
That is a good point.

连续,我的意思是该块可能包含单个换行符,但不包含连续的换行符。所以这不匹配:

Dear Sir:

That is a good point.

On Wednesday, June 1, John wrote:
> Hello world.

通过“发件人撰写的部分”,我的意思是电子邮件正文可能包含我要排除的回复或转发文本或签名(我们称之为“非原创内容”)。虽然野外可能有很多变化,但仅仅处理以下情况就足够了(现在):

1)以两个破折号开头的行(例如:-----转发的消息-----),因为签名在行的开头通常也有两个破折号

2)一行以“On”开头,后面跟一行以“>”开头抓住这种格式:

On Wednesday, June 1, John wrote:
> Hello world.

如果非原始块上方没有任何东西(没有非空白区域),则应该没有匹配。

最后,请记住,在消息的开头以及目标文本块和消息的结尾之间,或者在目标文本块和非文本块的开头之间可能存在任何数量的空白区域。原创内容。另外,请记住,电子邮件中的回车可能只是一个换行符或一个文件。

这是我的第一次尝试,比我开始写这篇文章的时候更接近;它使用s标志:

^\s*(\S[^(?:\n\n|\r\n\r\n)]*\S)\s*(?:$|(?:$|\-\-.*|On [^\n]*\n\>.*))

从我到目前为止的测试来看,如果目标文本只是一行,它似乎有效,但如果它不止一行则不行。因此,主要缺陷似乎在这一部分:

_______[^(?:\n\n|\r\n\r\n)]*________________________________________

更新:这是我正在使用的解决方案:

'/\A\s*((?:[^\r\n]+\r?(?:\n|\z))+)\s*(?:\z|(--.*|On .+:\n\>.*))/s'

请注意,“开”行可以换行到多行(例如,如果日期和电子邮件地址很长),但通常会有“:\ n>”在那里。

3 个答案:

答案 0 :(得分:3)

在您标记的部分中:

[^(?:\n\n|\r\n\r\n)]*

方括号表示字符类,而克拉将字符反转以匹配。所以我想正则表达式引擎正在构建一个与(不匹配的字符类,与?不匹配,与:不匹配,依此类推。

这是一个正则表达式,我相信你做了这个部分你想要的东西:

((?:[^\r\n]+\r?\n)*)

这意味着“匹配任何除了CR或LF之外的任何数字,但任何数字,但至少有一个,后跟CR,然后绝对是LF。然后当它由*重复时(零次或多次)它不会匹配连续的两个行结尾,因为模式的开头不是一个行结尾。然后整个事情就是用parens来创建一个匹配组。

现在,我们需要锚定它,以便它可以在您想要的位置。看起来你期待三个锚案例:字符串结束,“On write”行或签名行(“ - \ n”)。你的正则表达式比固定这三种情况更加复杂;这样做:

(?:$|--\r?\n|On \d\d/\d\d/\d\d\d\d \d\d:\d\d [AP]M, .*wrote:\r?\n)

它比你的更长,因为我想确保它不会锚定在实际的电子邮件消息文本上,该文本恰好以一行开头的“On”开头。

并且您在匹配组和锚点之间允许任意数量的空行:

(?:\r?\n)*

将这些放在一起:

((?:[^\r\n]+\r?\n)*)(?:\r?\n)*(?:$|--\r?\n|On \d\d/\d\d/\d\d\d\d \d\d:\d\d [AP]M, .*wrote:\r?\n)

我使用Python的re模块测试正则表达式,使用收件箱中的实际电子邮件对这些进行了测试。

注意:实际上,现在我考虑一下,我不建议使用这样严格的正则表达式来匹配“开”线。 “开”行由发件人正在使用的电子邮件客户端插入,您无法控制它。如果用户的电子邮件客户端插入24小时而不是AM / PM怎么办? (我甚至看到法国人的电子邮件客户端插入法语而不是“开”,所以整条线甚至都不匹配!)所以你可能想要一个更宽松的匹配模式用于“开”线,但要注意,如果它太宽松并且一封电子邮件中包含一条恰好以“开启”开头的行,您可能会提前砍掉。

这是一个应该有效的简单模式:

On \d[^\n]+\n>

开,然后是数字,然后是直到行结束的任何数字,但下一行必须以>开头。这应该有效,除了病理情况,电子邮件正文有一行以“开”和一个数字开头,然后下一行以单词“From”开头,所以电子邮件客户端之前插入> “从”。

无论如何,把它们放在一起:

((?:[^\r\n]+\r?\n)*)(?:\r?\n)*(?:$|--\r?\n|On \d[^\n]+\n>)

编辑:你让我做了一个快速编辑并用你的最终模式更新它,所以你走了:

/\A\s*((?:[^\r\n]+\r?(?:\n|\z))+)\s*(?:\z|(--.*|On [^\n]+\n\>.*))/s

答案 1 :(得分:0)

/^(?!>|On|--)(.*)+/m应匹配任何不以On开头的行,>或 -

答案 2 :(得分:0)

使用JavaScript .match()这应该与您的所有测试用例匹配:

/((.|[\r\n])+?)([\r\n][\r\n]|On.+[\r\n]\>|--)/

这意味着:启动正则表达式/后跟任意字符或换行符(.|[\r\n])一次或多次(+)不合理(?)后跟两个换行符([\r\n\r\n])或'在换行符>'或' - '([\r\n][\r\n]|On.+[\r\n]\>|--)后跟正则表达式结束(/)。

首先分组是您追求的字符串。

在此处查看演示:http://jsfiddle.net/57L5t/