电子邮件主题中的重音单词中断间距 - 我该如何阻止?

Posted

技术标签:

【中文标题】电子邮件主题中的重音单词中断间距 - 我该如何阻止?【英文标题】:accented words in email subject break spacing - how do I stop this? 【发布时间】:2010-11-20 14:42:24 【问题描述】:

我们有一个自定义的 php 电子邮件营销应用程序,还有一个有趣的问题: 如果邮件的主题行包含带有重音符号的单词,它会“吞下”它与下一个单词之间的空格。 一个例子:短语

Ángel Ríos escucha y sorprende

显示为(至少 gmail 和 lotus 笔记)为

ÁngelRíos escucha y sorprende

消息源中的特定行显示:

Subject: =?ISO-8859-1?Q?=C1ngel?= =?ISO-8859-1?Q?R=EDos?= escucha y sorprende

(半完整标题):

Delivered-To: me@gmail.com
Received: elided
Return-Path: <return@path>
Received: elided
Received: (qmail 23734 invoked by uid 48); 18 Aug 2009 13:51:14 -0000
Date: 18 Aug 2009 13:51:14 -0000
To: "Adriano" <me@gmail.com>
Subject: =?ISO-8859-1?Q?=C1ngel?= =?ISO-8859-1?Q?R=EDos?= escucha y sorprende
MIME-Version: 1.0
From: elided
X-Mailer: PHP
X-Lista: 1290
X-ID: 48163
Content-Type: text/html; charset="ISO-8859-1"
Content-Transfer-Encoding: quoted-printable
Message-ID: <kokrte.rpq06m@example.com>

编辑:

该应用程序使用旧版本的 Html Mime Mail 来准备消息,我会尝试升级到新版本。无论如何,这是对主题进行编码的函数:

/**
 * Function to encode a header if necessary
 * according to RFC2047
 */
function _encodeHeader($input, $charset = 'ISO-8859-1')

    preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
    foreach ($matches[1] as $value) 
        $replacement = preg_replace('/([\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
        $input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
    

    return $input;

这是主题被编码的代码:

if (!empty($this->headers['Subject'])) 
    $subject = $this->_encodeHeader($this->headers['Subject'],
                                    $this->build_params['head_charset']);
    unset($this->headers['Subject']);

总结

问题在于,确实,在提到的情况下,程序没有对空间进行编码。 The accepted answer 解决了我的问题,经过轻微修改(在该答案的 cmets 中提到),因为安装的 PHP 版本不支持特定的实现细节。

最终答案

虽然接受的答案确实解决了问题,但我们发现它与数千封电子邮件相结合,正在消耗服务器上的所有可用内存。我查看了这个邮件框架原开发者的网站,发现功能已经更新为如下:

function _encodeHeader($input, $charset = 'ISO-8859-1') 
        preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
        foreach ($matches[1] as $value) 
            $replacement = preg_replace('/([\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
            $input = str_replace($value, $replacement , $input);
        
        if (!empty($matches[1])) 
            $input = str_replace(' ', '=20', $input);
            $input = '=?' . $charset .  '?Q?' .$input . '?=';
        
        return $input;
    

巧妙地解决了问题并保持在内存限制之下。

【问题讨论】:

您使用什么(如果有的话)对主题行进行编码和/或发送电子邮件? 您是否也尝试对中间的空间进行编码?还是一次性完成整个主题? 【参考方案1】:

您还需要对两者之间的空间进行编码(请参阅RFC 2047):

(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)     (ab)

不显示相邻“编码词”之间的空白。

[…]

(=?ISO-8859-1?Q?a_b?=)                      (a b)

为了使 SPACE 显示在编码文本的一部分中,必须将 SPACE 编码为“编码字”的一部分。

(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)    (a b)

为了在两个编码文本字符串之间显示空格,可以将空格编码为“编码字”之一的一部分。

所以应该这样做:

Subject: =?ISO-8859-1?Q?=C1ngel=20R=EDos?= escucha y sorprende

编辑    试试这个功能:

function _encodeHeader($str, $charset='ISO-8859-1')

    $words = preg_split('/(\s+)/', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    $func = create_function('$match', 'return $match[0] === " " ? "_" : sprintf("=%02X", ord($match[0]));');
    $encoded = false;
    foreach ($words as $key => &$word) 
        if (!ctype_space($word)) 
            $tmp = preg_replace_callback('/[^\x21-\x3C\x3E-\x5E\x60-\x7E]/', $func, $word);
            if ($tmp !== $word) 
                if (!$encoded) 
                    $word = '=?'.$charset.'?Q?'.$tmp;
                 else 
                    $word = $tmp;
                    if ($key > 0) 
                        $words[$key-1] = preg_replace_callback('/[^\x21-\x3C\x3E-\x5E\x60-\x7E]/', $func, $words[$key-1]);
                    
                
                $encoded = true;
             else 
                if ($encoded) 
                    $words[$key-2] .= '?=';
                
                $encoded = false;
            
        
    
    if ($encoded) 
        $words[$key] .= '?=';
    
    return implode('', $words);

【讨论】:

谢谢!这成功了。如果发现特殊字符,我修改了 html mime 邮件源以对空格进行编码。 必须重新实现你使用 foreach $words as $key => &$word 并在 foreach 中使用 $words[$key] 的位。【参考方案2】:

添加

$input = str_replace('?', '=3F', $input);

在这个片段中:

if (!empty($matches[1])) 
$input = str_replace('?', '=3F', $input);
$input = str_replace(' ', '=20', $input);
$input = '=?' . $charset .  '?Q?' .$input . '?=';

【讨论】:

【参考方案3】:

查找 mbstring 和 UTF 转换。非英语语言中的许多特殊字符都在 UTF8 字符集中处理。

将您的主题字符串转换为 UTF8 并确保按原样发送电子邮件应正确呈现主题行。

至少当我们在发送电子邮件时遇到类似问题时它对我们有用

【讨论】:

【参考方案4】:

看来您最好发送 Subject: =?ISO-8859-1?Q?=C1ngel R=EDos escucha y sorprende?= ,因为问题出现在 ?= 编码端附近。

【讨论】:

以上是关于电子邮件主题中的重音单词中断间距 - 我该如何阻止?的主要内容,如果未能解决你的问题,请参考以下文章

如何解释 Python 中正则表达式的重音字符?

阻止通知电子邮件上的Gmail线程对话

为啥 Gmail 会阻止电子邮件中的 CSS?

我如何扩展 jquery quicksearch 以便它可以搜索带重音的单词?

如何阻止 if/else 中的 else 被执行?

使用 Javascript 正则表达式匹配重音字符