PHP Conference Japan 2024

quoted_printable_decode

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

quoted_printable_decode將 quoted-printable 字串轉換為 8 位元字串

說明

quoted_printable_decode(字串 $string): 字串

此函式會傳回一個 8 位元二進位字串,對應於已解碼的 quoted-printable 字串(根據 » 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 年前
如果您看到黑色菱形或奇怪的字元,這些字元似乎會阻擋 echo 輸出,但 strlen($string) 仍然大於 0,那麼您可能遇到了編碼問題。不像那些在解碼頁面上撰寫編碼函式的人,我將會在解碼頁面上實際討論解碼。

我遇到的特定問題是,一封電子郵件使用俄羅斯編碼 (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 內建函式,則可以直接使用,而這段程式碼將不會執行任何動作。

<?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

函式 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 編碼測試字串", $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 年前
請注意,以下的編碼函式中有一個錯誤!

<?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
This function enables you to convert text to a quoted-printable string as well as to create encoded-words used in email headers (see http://www.faqs.org/rfcs/rfc2047.html).

No line of returned text will be longer than specified. Encoded-words will not contain a newline character. Special characters are removed.
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('Input string is not valid 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) // we need encoded word to be single line
: preg_replace('~\r\n?~', "\n", $string); // normalize line endings
$string = preg_replace('~[\x00-\x08\x0B-\x1F]+~', '', $string); // remove control characters

$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 // high byte value
$ord === 95 or // underscore "_"
$ord === 63 && $encodedWord or // "?" in encoded word
$ord === 61 or // equal sign "="
// space or tab in encoded word or at line end
$ord === 32 || $ord === 9 and $encodedWord || !isset($nextOrd) || $nextOrd === 10
) {
$chr = sprintf('=%02X', $ord);
}

if (
$ord === 10) { // line feed
$output .= $endl;
$charsLeft = $lineLength;
} elseif (
strlen($chr) < $charsLeft or
strlen($chr) === $charsLeft and $nextOrd === 10 || $encodedWord
) { // add character
$output .= $chr;
$charsLeft-=strlen($chr);
} elseif (isset(
$nextOrd)) { // another line needed
$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 年前
我使用一個方法來處理這個錯誤

$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 encode 版本,因為 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="";
}
}
$line = ""; // Added: Reset $line after reaching 75 characters
} // Corrected: Moved closing brace to correct position. The original code was missing a closing brace, resulting in a logical error.
}
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 (保留空格和 Tab 字元)
$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);
}
naitsirch at e dot mail dot de
4 年前
如果傳遞給 quoted_printable_decode 的字串中包含 NULL 位元組,則該函式會截斷 NULL 位元組及其後的所有內容。

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

這並非錯誤,而是預期行為,並由 RFC 2045(參見 https://www.ietf.org/rfc/rfc2045.txt)的第 2.7 和 2.8 段落所定義。
To Top