2024年PHP日本会议

quoted_printable_decode

(PHP 4, PHP 5, PHP 7, PHP 8)

quoted_printable_decode将quoted-printable字符串转换为8位字符串

描述

quoted_printable_decode(字符串 $string): 字符串

此函数返回与已解码的quoted-printable字符串对应的8位二进制字符串(根据» RFC2045,第6.7节,而不是» RFC2821,第4.5.2节,因此不会从行首去除额外的句点)。

此函数类似于imap_qprint(),不同之处在于此函数不需要IMAP模块即可工作。

参数

字符串

输入字符串。

返回值

返回8位二进制字符串。

示例

示例 #1 quoted_printable_decode() 示例

<?php

$encoded
= quoted_printable_encode('Möchten Sie ein paar Äpfel?');

var_dump($encoded);
var_dump(quoted_printable_decode($encoded));
?>

以上示例将输出

string(37) "M=C3=B6chten Sie ein paar =C3=84pfel?"
string(29) "Möchten Sie ein paar Äpfel?"

参见

添加注释

用户贡献的注释 21条注释

jab_creations at yahoo dot com
4年前
如果您看到黑色菱形或奇怪的字符,这些字符似乎阻止了回显,但仍然遇到strlen($string) > 0,则您可能遇到了编码问题。与在DECODE页面上编写ENCODE函数的人不同,我将实际在DECODE页面上讨论DECODE。

我遇到的具体问题是,一封电子邮件使用俄语编码(KOI8-R)进行编码,但我将所有内容都输出为UTF-8,因为:兼容性。

如果您尝试使用俄语编码执行此操作

<?php
echo quoted_printable_decode('=81');
?>

您将获得损坏的数据。

我做了一些测试,结果发现嵌套mb_convert_encoding函数的方法如下所示

<?php
echo '<p>Test: "'.mb_convert_encoding(quoted_printable_decode('=81'), 'UTF-8', 'KOI8-R').'".</p>';
?>

不幸的是,我在RFC 2045第6.7节中找不到字符映射表或任何列出的内容。但是我偶然发现了网站https://dencode.com/en/string/quoted-printable,它允许您手动选择编码(这是一个开源站点,对于好奇的人来说,他们有一个GIT存储库)。

事实证明,范围的开始与编码有关。因此,拉丁语(ISO-8859-1)和俄语(KOI8-R)可能会(未经测试)编码为不同的字符,**相对于字符串编码**。
Karora
16年前
将前面的一些评论放在一起,您可以综合出一个简洁高效的quoted_printable_encode函数,如下所示

请注意,我将其放在我的标准库文件中,因此我将其包装在一个!function_exists中,以便如果存在预先存在的PHP函数,它将正常工作,并且此函数将计算为noop。

<?php
if ( !function_exists("quoted_printable_encode") ) {
/**
* 处理字符串以符合RFC2045第6.7节的要求。请注意
* 此方法有效,但替换的字符多于最小集合。为了可读性
* 空格不会被编码为=20。
*/
function quoted_printable_encode($string) {
return
preg_replace('/[^\r\n]{73}[^=\r\n]{2}/', "$0=\r\n", str_replace("%","=",str_replace("%20"," ",rawurlencode($string))));
}
}
?>

此致,
Andrew McMillan。
madmax at express dot ru
24年前
某些浏览器(例如Netscape)
发送8位quoted-printable文本如下所示
=C5=DD=A3=D2=C1= =DA

"= ="表示连续的单词。
php函数未检测到这种情况,并将其转换为如下字符串
abcde=f
soletan at toxa dot de
17年前
警告!以下编码文本的方法不符合RFC1521的要求!

考虑一下由75个“A”和单个é(或类似的非ASCII字符)组成的行……以下方法将编码并返回78个八位字节的行,这与RFC 1521,5.1规则#5相冲突:“Quoted-Printable编码要求编码行长度不超过76个字符。”

好的QP编码需要更多内容。
zg
16年前
<?php

function quoted_printable_encode( $str, $chunkLen = 72 )
{
$offset = 0;

$str = strtr(rawurlencode($str), array('%' => '='));
$len = strlen($str);
$enc = '';

while (
$offset < $len )
{
if (
$str{ $offset + $chunkLen - 1 } === '=' )
{
$line = substr($str, $offset, $chunkLen - 1);
$offset += $chunkLen - 1;
}
elseif (
$str{ $offset + $chunkLen - 2 } === '=' )
{
$line = substr($str, $offset, $chunkLen - 2);
$offset += $chunkLen - 2;
}
else
{
$line = substr($str, $offset, $chunkLen);
$offset += $chunkLen;
}

if (
$offset + $chunkLen < $len )
$enc .= $line ."=\n";
else
$enc .= $line;
}

return
$enc;
}

?>
pob at medienrecht dot NOSPAM dot org
23年前
如果您无法访问imap_*函数,并且不想使用
?$message = chunk_split( base64_encode($message) );?
因为您希望能够阅读邮件的“源代码”,您可能想尝试这个方法
(非常欢迎任何建议!)


function qp_enc($input = "quoted-printable encoding test string", $line_max = 76) {

$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
$lines = preg_split("/(?:\r\n|\r|\n)/", $input);
$eol = "\r\n";
$escape = "=";
$output = "";

while( list(, $line) = each($lines) ) {
//$line = rtrim($line); // 删除尾随空格 -> 不需要 =20\r\n
$linlen = strlen($line);
$newline = "";
for($i = 0; $i < $linlen; $i++) {
$c = substr($line, $i, 1);
$dec = ord($c);
if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // 只转换行尾的空格
$c = "=20";
} elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // 始终编码“\t”,这其实并非必需
$h2 = floor($dec/16); $h1 = floor($dec%16);
$c = $escape.$hex["$h2"].$hex["$h1"];
}
if ( (strlen($newline) + strlen($c)) >= $line_max ) { // 不计算CRLF
$output .= $newline.$escape.$eol; // 软换行;“ =\r\n”是可以的
$newline = "";
}
$newline .= $c;
} // for循环结束
$output .= $newline.$eol;
}
return trim($output);

}

$eight_bit = "\xA7 \xC4 \xD6 \xDC \xE4 \xF6 \xFC \xDF = xxx yyy zzz \r\n"
." \xA7 \r \xC4 \n \xD6 \x09 ";
print $eight_bit."\r\n---------------\r\n";
$encoded = qp_enc($eight_bit);
print $encoded;
legolas558
17年前
请注意,下面的编码函数中存在一个bug!

<?php
if (($c==0x3d) || ($c>=0x80) || ($c<0x20))
?>

$c应该检查是否小于或等于空格的编码!

所以正确的代码是

<?php
if (($c==0x3d) || ($c>=0x80) || ($c<=0x20))
?>

请修复代码或发布此注释
Christian Albrecht
16年前
除了david lionhead的函数之外

<?php
function quoted_printable_encode($txt) {
/* 确保没有%20或类似字符 */
$txt = rawurldecode($txt);
$tmp="";
$line="";
for (
$i=0;$i<strlen($txt);$i++) {
if ((
$txt[$i]>='a' && $txt[$i]<='z') || ($txt[$i]>='A' && $txt[$i]<='Z') || ($txt[$i]>='0' && $txt[$i]<='9')) {
$line.=$txt[$i];
if (
strlen($line)>=75) {
$tmp.="$line=\n";
$line="";
}
}
else {
/* 重要:将此情况与上面情况区分开来 */
if (strlen($line)>=72) {
$tmp.="$line=\n";
$line="";
}
$line.="=".sprintf("%02X",ord($txt[$i]));
}
}
$tmp.="$line\n";
return
$tmp;
}
?>
vita dot plachy at seznam dot cz
16年前
<?php
$text
= <<<EOF
此函数允许您将文本转换为可打印引号字符串,以及创建用于电子邮件标题的编码字(参见 http://www.faqs.org/rfcs/rfc2047.html)。

返回文本的每一行长度都不会超过指定的长度。编码字不会包含换行符。特殊字符将被移除。
EOF;

define('QP_LINE_LENGTH', 75);
define('QP_LINE_SEPARATOR', "\r\n");

function
quoted_printable_encode($string, $encodedWord = false)
{
if(!
preg_match('//u', $string)) {
throw new
Exception('输入字符串不是有效的 UTF-8');
}

static
$wordStart = '=?UTF-8?Q?';
static
$wordEnd = '?=';
static
$endl = QP_LINE_SEPARATOR;

$lineLength = $encodedWord
? QP_LINE_LENGTH - strlen($wordStart) - strlen($wordEnd)
:
QP_LINE_LENGTH;

$string = $encodedWord
? preg_replace('~[\r\n]+~', ' ', $string) // 编码字需要单行
: preg_replace('~\r\n?~', "\n", $string); // 规范化换行符
$string = preg_replace('~[\x00-\x08\x0B-\x1F]+~', '', $string); // 移除控制字符

$output = $encodedWord ? $wordStart : '';
$charsLeft = $lineLength;

$chr = isset($string{0}) ? $string{0} : null;
$ord = ord($chr);

for (
$i = 0; isset($chr); $i++) {
$nextChr = isset($string{$i + 1}) ? $string{$i + 1} : null;
$nextOrd = ord($nextChr);

if (
$ord > 127 or // 高字节值
$ord === 95 or // 下划线 "_"
$ord === 63 && $encodedWord or // 编码字中的 "?"
$ord === 61 or // 等号 "="
// 编码字中或行尾的空格或制表符
$ord === 32 || $ord === 9 and $encodedWord || !isset($nextOrd) || $nextOrd === 10
) {
$chr = sprintf('=%02X', $ord);
}

if (
$ord === 10) { // 换行符
$output .= $endl;
$charsLeft = $lineLength;
} elseif (
strlen($chr) < $charsLeft or
strlen($chr) === $charsLeft and $nextOrd === 10 || $encodedWord
) { // 添加字符
$output .= $chr;
$charsLeft-=strlen($chr);
} elseif (isset(
$nextOrd)) { // 需要另起一行
$output .= $encodedWord
? $wordEnd . $endl . "\t" . $wordStart . $chr
: '=' . $endl . $chr;
$charsLeft = $lineLength - strlen($chr);
}

$chr = $nextChr;
$ord = $nextOrd;
}

return
$output . ($encodedWord ? $wordEnd : '');
}

echo
quoted_printable_encode($text/*, true*/);
andre at luyer dot nl
16年前
对Andrew下面代码的一个小更新。这个保留了原始的CRLF对(并允许preg_replace按预期工作)

<?php
if (!function_exists("quoted_printable_encode")) {
/**
* 处理字符串以符合RFC2045第6.7节的要求。请注意,
* 此方法有效,但替换的字符比最小集合多。为了可读性,
* 空格和CRLF对不会被编码。
*/
function quoted_printable_encode($string) {
return
preg_replace('/[^\r\n]{73}[^=\r\n]{2}/', "$0=\r\n",
str_replace("%", "=", str_replace("%0D%0A", "\r\n",
str_replace("%20"," ",rawurlencode($string)))));
}
}
?>

此致,André
Thomas Pequet / Memotoo.com
18年前
如果您想要一个执行“quoted_printable_decode()”反向操作的函数,请点击链接,您将找到“quoted_printable_encode()”函数
http://www.memotoo.com/softs/public/PHP/quoted printable_encode.inc.php

兼容"ENCODING=QUOTED-PRINTABLE"
示例
quoted_printable_encode(ut8_encode("c'est quand l'été ?"))
-> "c'est quand l'=C3=A9t=C3=A9 ?"
sven at e7o dot de
4年前
如果您真的偷懒,并且最终生成HTML,只需将其转换为HTML实体,并将Unicode/ISO的处理转移到文档的编码

<?php
function qpd($e)
{
return
preg_replace_callback(
'/=([a-z0-9]{2})/i',
function (
$m) {
return
'&#x00' . $m[1] . ';';
},
$e
);
}
?>
yual at inbox dot ru
11年前
我用了一个hack来解决这个bug

$str = str_replace("=\r\n", '', quoted_printable_encode($str));
if (strlen($str) > 73) {$str = substr($str,0,74)."=\n".substr($str,74);}
steffen dot weber at computerbase dot de
19年前
由于两位十六进制表示应该大写,因此您应该使用“=%02X”(大写X)而不是“=%02x”作为sprintf()的第一个参数。
feedr
15年前
另一个(改进的)quoted_printable_encode()版本。请注意str_replace()中数组元素的顺序。
我重写了之前的函数以提高可读性。

<?php
if (!function_exists("quoted_printable_encode")) {
/**
* 处理字符串以符合RFC2045第6.7节的要求。请注意,
* 此方法有效,但替换的字符比最小集合多。为了可读性,
* 空格和CRLF对不会被编码。
*/
function quoted_printable_encode($string) {
$string = str_replace(array('%20', '%0D%0A', '%'), array(' ', "\r\n", '='), rawurlencode($string));
$string = preg_replace('/[^\r\n]{73}[^=\r\n]{2}/', "$0=\r\n", $string);

return
$string;
}
}
?>
david at lionhead dot nl
16年前
我的quoted_printable编码版本,因为convert.quoted-printable-encode过滤器在outlook express上会中断。这个版本似乎在express/outlook/thunderbird/gmail上都能正常工作。

function quoted_printable_encode($txt) {
$tmp="";
$line="";
for ($i=0;$i<strlen($txt);$i++) {
if (($txt[$i]>='a' && $txt[$i]<='z') || ($txt[$i]>='A' && $txt[$i]<='Z') || ($txt[$i]>='0' && $txt[$i]<='9'))
$line.=$txt[$i];
else
$line.="=".sprintf("%02X",ord($txt[$i]));
if (strlen($line)>=75) {
$tmp.="$line=\n";
$line="";
}
}
$tmp.="$line\n";
return $tmp;
}
ludwig at gramberg-webdesign dot de
17年前
我使用流转换功能实现quoted printable编码的方法

<?php
/**
* @param string $str
* @return string
* */
function quoted_printable_encode($str) {
$fp = fopen('php://temp', 'w+');
stream_filter_append($fp, 'convert.quoted-printable-encode');
fwrite($fp, $str);
fseek($fp, 0);
$result = '';
while(!
feof($fp))
$result .= fread($fp, 1024);
fclose($fp);
return
$result;
}
?>
roelof
17年前
我修改了legolas558 at users dot sausafe dot net的以下版本,并添加了换行选项。

<?php
/**
* 将字符串编码为所谓的“quoted printable”。这种编码类型
* 用于将8位电子邮件内容作为7位发送。
*
* @access public
* @param string $str 要编码的字符串
* @param bool $wrap 在74个字符后添加换行符?
* @return string
*/

function quoted_printable_encode($str, $wrap=true)
{
$return = '';
$iL = strlen($str);
for(
$i=0; $i<$iL; $i++)
{
$char = $str[$i];
if(
ctype_print($char) && !ctype_punct($char)) $return .= $char;
else
$return .= sprintf('=%02X', ord($char));
}
return (
$wrap === true)
?
wordwrap($return, 74, " =\n")
:
$return;
}

?>
legolas558 at users dot sausafe dot net
17年前
正如soletan at toxa dot de所报告的那样,该函数非常糟糕,无法提供有效的quoted printable字符串。在使用它时,我看到垃圾邮件代理将电子邮件标记为QP_EXCESS,有时电子邮件客户端根本无法识别标头;我浪费了很多时间:(。这是新版本(我们在Drake CMS核心代码中使用它),它可以无缝工作

<?php

//L: 注意$encoding是大写的
//L: 此外,您的PHP安装必须具有ctype_alpha,否则请自行编写
function quoted_printable_encode($string, $encoding='UTF-8') {
// 将此函数与标头一起使用,而不要与电子邮件正文一起使用,因为它缺少换行
$len = strlen($string);
$result = '';
$enc = false;
for(
$i=0;$i<$len;++$i) {
$c = $string[$i];
if (
ctype_alpha($c))
$result.=$c;
else if (
$c==' ') {
$result.='_';
$enc = true;
} else {
$result.=sprintf("=%02X", ord($c));
$enc = true;
}
}
//L: 因此垃圾邮件代理不会将您的电子邮件标记为QP_EXCESS
if (!$enc) return $string;
return
'=?'.$encoding.'?q?'.$result.'?=';
}

希望对您有所帮助 ;)

?>
dmitry at koterov dot ru
19年前
之前的评论有一个错误:由于preg_match_all()的使用不正确,短测试的编码不起作用。有人读过它吗? :-)

正确的版本(似乎是),带有附加的imap_8bit()函数模拟

if (!function_exists('imap_8bit')) {
function imap_8bit($text) {
return quoted_printable_encode($text);
}
}

function quoted_printable_encode_character ( $matches ) {
$character = $matches[0];
return sprintf ( '=%02x', ord ( $character ) );
}

// 基于 http://www.freesoft.org/CIE/RFC/1521/6.htm
function quoted_printable_encode ( $string ) {
// 规则 #2, #3 (保留空格和制表符)
$string = preg_replace_callback (
'/[^\x21-\x3C\x3E-\x7E\x09\x20]/',
'quoted_printable_encode_character',
$string
);
$newline = "=\r\n"; // '=' + CRLF (规则 #4)
// 确保行的分割不会干扰转义字符
// (chunk_split在这里失败)
$string = preg_replace ( '/(.{73}[^=]{0,3})/', '$1'.$newline, $string);
return $string;
}
naitsirch at e dot mail dot de
4年前
如果传递的字符串中存在空字节,则quoted_printable_decode将裁剪空字节之后的所有内容以及空字节本身。

<?php
$result
= quoted_printable_decode("This is a\0 test.");
// $result === 'This is a'
?>

这不是一个bug,而是预期的行为,由RFC 2045 (参见 https://www.ietf.org/rfc/rfc2045.txt) 的2.7和2.8段落定义。
To Top