【问题标题】:Removing additional line-breaks from email body从电子邮件正文中删除额外的换行符
【发布时间】:2015-10-20 15:33:30
【问题描述】:

发送电子邮件时,许多服务器会添加额外的换行符以限制每行的长度。

在 PHP 脚本中提取电子邮件时如何恢复 原始 换行符?

示例

假设我发送以下内容:

Lorem ipsum Dolore incididunt in culpa ea ea sed quis sint voluptate quis laborum ullamco Exceptioneur do adipisicing consequat ex in reprehenderit officia in ad deserunt magna nulla dolor labourum occaecat reprehenderit aliquip dolor ea anim ea in veniam adipisicing culpa tempor qui Laboris minim consectetur laboris anim incididunt Ut sunt sunt mollit elit irure do cillum dolore consequat in ea culpa ut velit sunt nulla in dolore voluptate dolore laborum reprehenderit dolore ut.
Ut non in veniam enim minim elit ad ut id ad eu voluptate cillum dolor laboris irure tempor mollit dolore exercitation eiusmod ea non ea ullamco nostrud cillum nostrud labourum commodo esse reprehenderit ut deserunt officia do in anim dolore ullamco pariatur ex amet nulla excepteur mollit officia fugiat eu sed quis nisi fugiat dolor ea commodo ut sunt in consectetur ut nulla pariatur est dolor dolore non ut occaecat officia Duis Ut ex exercitation esse ullamco nulla incididunt commodo pariatur dolore nostrud fugiat id dolor minim non sint amet adipisicing occaecat enim non Ut ad nisi ut commodo minim proident elit nulla quis ut ad dolor Exceptioneur dolore Duis。

请注意,此文本中只有一个换行符!

在接收端使用Thunderbird查看邮件源代码,或者通过PHP获取邮件正文,内容格式如下:

Lorem ipsum Dolore incididunt in culpa ea ea ea sed quis sint voluptate
quis labourum ullamco Exceptioneur 做 adipisicing consequat ex in
在 ad deserunt magna nulla dolor labourum occaecat 中谴责官员
reprehenderit aliquip dolor ea 动画 ea in veniam adipisicing culpa
tempor qui elit voluptate consectetur elit laboris minim consectetur
Laboris anim incididunt Ut sunt sunt mollit elit irure do cillum dolore
导致 ea culpa ut velit sunt nulla 在 dolore voluptate dolore
labourum reprehenderit dolore ut.
Ut non in veniam enim minim elit ad ut id ad eu voluptate cillum dolor
Laboris irure tempor mollit dolore exercitation eiusmod ea non ea
ullamco nostrud cillum nostrud laborum commodo esse reprehenderit ut
deserunt officia do in anim dolore ullamco pariaatur ex amet nulla
除了 mollit officia fugiat eu sed quis nisi fugiat dolor ea commodo
ut sunt in consequat consectetur ut nulla pariaatur est dolor dolore non
ut occaecat officia Duis Ut exercitation esse ullamco nulla
事件 commodo pariatur dolore nostrud fugiat id dolor minim non
sint amet adipisicing occaecat enim non Ut ad irure sint aliquip nisi ut
commodo minim proident elit nulla quis ut ad dolor Exceptioneur dolore Duis。

请注意,每行都有一定的长度,因此存在 16 个额外的换行符。这些额外的换行符会自动添加到导致我收到电子邮件的事件链中的某个位置。

我希望我的电子邮件提取 PHP 脚本删除额外的换行符以恢复内容的原始两行格式。

我知道新的换行符不是由 PHP 脚本添加的,我知道它们来自哪里,但我不知道如何让我的 PHP 脚本删除这些换行符。

这是用于获取电子邮件正文的代码:

$connection = imap_open(
    sprintf(
        '{%s:110/pop3}INBOX',
        Configure::read('Email.Inbox.host')
    ),
    Configure::read('Email.Inbox.email'),
    Configure::read('Email.Inbox.password')
);

$mailbox = imap_check($connection);
$messages = imap_fetch_overview($connection, '1:' . $mailbox->Nmsgs); 

foreach($messages as $message) {
    $content = imap_fetchbody($connection, $message->msgno, 1);
}

我尝试了什么?

我尝试使用imap_body 而不是imap_fetchbody,因为前者不处理电子邮件正文。但是在此之前已经存在额外的换行符,并且与常规换行符没有区别。两者都包含\r\n

我认为必须有一种方法可以做到这一点,因为 Thunderbird 以正确的格式显示收到的电子邮件,没有额外的 16 个换行符,尽管它们存在于所显示消息的源代码中。因此,可能必须有一种方法从电子邮件中删除额外的 16 个换行符。

这是 Thunderbird 的屏幕截图,顶部显示电子邮件的源代码,底部显示生成的纯文本。

【问题讨论】:

  • 你有format=flowed,这意味着如果邮件客户端愿意,它可以忽略换行符。
  • @MarcB:这正是我的问题!使用 PHP 获取电子邮件时如何忽略额外的换行符?执行str_replace('\r\n', '', $content); 之类的操作将替换所有换行符,而不仅仅是自动添加的换行符。
  • php 不会添加任何新的换行符。它会准确地吐出从 imap 服务器返回的内容。由您自己编写代码以将该文本重新格式化为您想要的任何格式。
  • @MarcB 让我来说明一下我的问题:Thunderbird 和我的 PHP 脚本都从 IMAP 服务器接收相同的内容。在这两种情况下,该内容都包含在某些时候添加的额外换行符。 Thunderbird 显然对获取的内容做了一些魔术来删除添加的换行符。我希望我的 PHP 脚本能够做到这一点。它应该从 IMAP 服务器接收 liney-breakey-content 并过滤掉发送电子邮件时不存在的所有换行符。但是我不知道怎么做!我的问题只是“如何?”
  • 是的,因为 TB 有一堆代码来处理格式化文本以供显示。 PHP 没有。它不是邮件客户端,由您来提供代码/逻辑来复制 TB 正在做的任何事情。电子邮件只是文本,所以无论你最终做什么都将涉及字符串操作。

标签: php email line-breaks


【解决方案1】:

尽管这个问题很老,但当我遇到这个完全相同的问题时,它是最热门的问题之一。正如 Marc 在 cmets 中指出的那样,它确实与 format=flowed 有关。所以我深入研究了 RFC 2646 并找到了 section 4.1Generating Format=Flowed

因为软换行符是一个 SP CRLF 序列,生成代理通过在出现空格后插入一个 CRLF 来创建一个。

生成代理不应该在单词中插入空格(不包含空格的可打印字符序列)。如果遇到超过 79 个字符(但小于 998 个字符,[SMTP] 行长限制)的单词,代理应该按原样发送单词并超过行长限制为 79 个字符。

因此,为了获得最初编写的电子邮件,只需搜索所有出现的 SP+CRLF 并将其替换为空即可。然后您可能还想撤消空格填充,同时还要考虑引用的文本(以任意数量的 > 字符开头的行,后跟一个空格)。根据RFC,测试的顺序是引号>空格填充>流水线:

在接收时,如果一行的第一个字符是空格,它会被逻辑删除。这发生在对引用的行进行测试之后,在对流动的行进行测试之前。

我自己厨房的粗略 PoC:

// I'm using fetchmime() because I want to be sure I'm getting the proper MIME type for the relevant section
$mimes = imap_fetchmime($connection, $message->msgno, $section);

// I don't want to store all headers in an array since I just want to know the Content-Type
// [ \t]* is probably not necessary but it's there in case of broken clients/servers
if(preg_match('/^[ \t]*Content-Type.*format=flowed\b/mi', $mimes)) {
    // First, let's undo space stuffing but don't touch stuffed lines with quotes
    $content = preg_replace('/^ +(?!>+ )/m', '', $content);

    // Then, remove flowed SP+(CR)LF sequences as well as any possible quotation marks that might appear after it to reform one long line of text
    $content = preg_replace('/( )\r?\n(>+ +)?/', '$1', $content);

    // Remove empty quoted lines at *the end of the string only*, keeping any such lines anywhere else as-is for readability
    $content = preg_replace('/(\r?\n>+\s*)+$/', '', $content);
}
// And finally trim the entire thing (regardless of formatting)
$content = trim($content);
// Or when outputting to browsers:
//$content = nl2br(trim($content));

对我来说,这适用于:

  • 简单的单行电子邮件
  • OP 给出的 lorem ipsum 示例有 2 个段落
  • 单行后跟 2 个换行符和一个由 2 行组成的签名
  • 带有最多 4 个级别的引用的电子邮件(可能超过,但我没有费心检查那么远)

【讨论】:

  • 欢迎来到 StackOverflow,非常感谢您抽出宝贵时间回答我的问题!看起来这确实有效(我了解了 SP CRLF)。
  • 没问题。 =] 虽然正则表达式 mime 类型字符串有点脏,但同时我找到了一个更清洁的解决方案。它使用imap_fetchstructure($connection, $message->msgno),我没有看到您在原始代码中使用它,因此它可能不适合您。只需检查 $structure->ifparameters 是否为 1,然后遍历 $structure->parameters 并检查 strtolower($param->attribute) == 'format' && strtolower($param->value) == 'flowed'
猜你喜欢
  • 2014-05-21
  • 1970-01-01
  • 2010-12-20
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 1970-01-01
  • 2016-10-18
  • 1970-01-01
相关资源
最近更新 更多