【问题标题】:Regex to extract new content from email body正则表达式从电子邮件正文中提取新内容
【发布时间】:2011-09-21 16:05:38
【问题描述】:

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

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.

如果非原始块上方没有任何内容(没有非空白),则不应该有匹配。

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

这是我的第一次尝试,比我开始写这篇文章时的想法更接近;它使用 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'

请注意,“On”行可能会换成多行(例如,如果日期和电子邮件地址很长),但通常会有一个“:\n>”。

【问题讨论】:

  • 嗯...我的代码块中的蓝色不是故意的。
  • @dlo lang-none

标签: regex


【解决方案1】:

在您标记的部分:

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

方括号表示一个字符类,而克拉倒置字符以匹配。所以我想正则表达式引擎正在构建一个不匹配(、不匹配?、不匹配:等等的字符类。

这是一个正则表达式,我相信它可以满足您对这部分的要求:

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

这意味着“匹配除 CR 或 LF 之外的任何数字,除至少一个之外的任何数字,后跟可选的 CR,然后肯定是 LF。然后当它重复 *(零次或多次)时,它赢了' 不匹配一行中的两个行尾,因为模式的开头不是行尾。然后整个事情都在括号中以构成一个匹配组。

现在,我们需要将其锚定,以便它到达您想要的位置。看起来您期待三种锚定情况:字符串结尾、“写入时”行或签名行(“--\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 模块来测试正则表达式。

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

这是一个应该可以工作的简单模式:

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

开,后跟一个数字,然后是任何内容,直到行尾,但下一行必须以 > 开头。这应该可行,除了电子邮件正文有一行以“On”和一个数字开头的病态情况,然后下一行以“From”一词开头,因此电子邮件客户端在“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

【讨论】:

  • 感谢您的彻底回复。你对匹配完整的 On 字符串的热情给我留下了深刻的印象,但我同意这太严格了。我也处理国际电子邮件,将来我可能会修改正则表达式以处理其他几种语言,但现在 On case 就足够了。
  • 您的解决方案很接近,但并不完全正确。如果“新内容”块有多个段落,则正则表达式将匹配最后一个 - 但我根本不希望它匹配。所以在上面我说不应该匹配的例子中,你的返回“这是一个好点”。我认为您给了我足够的帮助,可以从这里弄清楚,当我这样做时我会发布更新......但也许你会打败我。
  • 我远离我的电脑(使用我的手机),所以我不会编写任何代码,但我建议您将模式锚定在标题行上。在匹配组之前匹配标题行,如果其中有多余的行,则匹配失败。
  • 其实我需要做的修改非常小:/\A\s*((?:[^\r\n]+\r?(?:\n|\z) )+)\s*(?:\z|(--.*|On [^\n]+\n\>.*))/s 初始的 '\A\s*' 是从字符串的开头,我将您的初始 * 更改为 + 因为至少需要一行。 '\z' 用于处理字符串结尾没有任何结尾 \n 的情况(可能永远不会在电子邮件中发生,但以防万一)。
  • 我还将其更改为“非原创内容”的参考,以便我可以轻松判断是否有任何内容。我添加了 's' 标志,以便点可以匹配所有后续内容,包括 \n。如果您对答案进行快速编辑,我会接受。谢谢——我现在对正则表达式的理解好多了。
【解决方案2】:

/^(?!>|On|--)(.*)+/m 应该匹配任何不以 On、> 或 --

开头的行

【讨论】:

    【解决方案3】:

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

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

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

    第一个分组是你要的字符串。

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

    【讨论】:

      猜你喜欢
      • 2021-01-17
      • 2011-01-16
      • 1970-01-01
      • 1970-01-01
      • 2017-09-25
      • 2017-07-13
      • 2021-11-24
      • 2018-01-19
      • 1970-01-01
      相关资源
      最近更新 更多