淺談 MIME data 編碼與解碼 (encode / decode)

Posted on  Nov 30, 2017  in  資訊技術探索  by  Amo Chen  ‐ 2 min read

一般處理電子郵件時,如果是全部都使用 ASCII 字元的電子郵件倒還相當直覺,一旦遇到中文(Big5)或其他種語系的時候,就一定會遇到郵件標頭(message headers)內的資訊(如標題 / subject)是經過編碼的,如以下例子的 Subject

Delivered-To: [email protected]
From: =?Big5?B?pKS12KrAt3y61qdRwXCmWMRVttKo87d8?=
<[email protected]>
Subject: =?Big5?B?reyo06FBpc2k6cRAseazb7zLpc6n86fWvNahSQ==?=
To: [email protected]
Content-Type: multipart/alternative;
boundary="=_NextPart_gfa16yplmtrdgwhukyrk8"
MIME-Version: 1.0
Reply-To: [email protected]
Date: Wed, 11 Feb 2011 17:23:24 +0800

上述例子 Subject 被編碼為 =?Big5?B?reyo06FBpc2k6cRAseazb7zLpc6n86fWvNahSQ==?= 。這是因為在 MIME(Multipurpose Internet Mail Extensions) RFC2047 標準中規範了郵件標頭內 非ASCII 的字元必須被轉成能以 ASCII 表達的字串 ,此種經過編碼的字串會以 =? 作為開頭,以 ?= 作為結尾,以 ? 分隔,整個字串裡會包含的資訊依序為語系、編碼方式、編碼後的字串,如下所示:

=? 語系 ? 編碼方式 ? 編碼後的字串 ?=

其中, 編碼方式分為 Base64 及 Quoted-Printable 兩種,分別以 b 及 q 作為表示(大小寫皆可) 。因此上述的 =?Big5?B?reyo06FBpc2k6cRAseazb7zLpc6n86fWvNahSQ==?= ,可解讀為 reyo06FBpc2k6cRAseazb7zLpc6n86fWvNahSQ= 是經過 Base64 編碼過後的 Big5 語系的字串。

因為中文字不是 ASCII 字元,所以當你遇到電子郵件無法正常顯示中文時,就看看是不是該用 MIME 的標準進行編碼。

Python

Python 已經內建好 email 模組可以使用,所以可以運用 email 模組幫忙 encode / decode ,例如:

from email.header import Header
print(Header('我是中文', 'utf-8').encode())
# =?utf-8?b?5oiR5piv5Lit5paH?=

from email.header import decode_header
print(decode_header('=?utf-8?b?5oiR5piv5Lit5paH?='))
# [(b'\xe6\x88\x91\xe6\x98\xaf\xe4\xb8\xad\xe6\x96\x87', 'utf-8')]

上述就是利用 Header.encode & decode_header 幫忙 encode / decode ,其中 decode_header 會把 decode 出來的文字回傳存有多個 (text, charset) tuple 結構的 list 。

另外,如果想要用 Quoted-Printable 編碼方式的話,可以用 quopri 模組。

PHP

如果以 PHP 內建的 function decode ,可以簡單使用 iconv_mime_decode() decode ,使用方式如下:

不過這樣的方式還是可能會有問題產生,例如字串內 同時包含 ASCII 字元及非 ASCII 字元的情況下,就會導致 iconv_mime_decode() 無法順利解譯,而回傳(return) false 。

這時候還可以使用 imap_mime_header_decode() 來輔助除錯,此函數會回傳一個陣列類別(array object),裡面每一個元素都會包含兩種屬性 charset, text ,例如:

<?php
$text = "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <[email protected]>";

$elements = imap_mime_header_decode($text);
for ($i=0; $i<count($elements); $i++) {
    echo "Charset: {$elements[$i]->charset}\n";
    echo "Text: {$elements[$i]->text}\n\n";
}
?>

輸出結果:

Charset: ISO-8859-1
Text: Keld Jørn Simonsen

Charset: default
Text:  <[email protected]>

上述 charset 輸出分別為 ISO-8859-1, default ,其中 default 就是指 ASCII 編碼。

p.s. imap_mime_header_decode() 需安裝 imap 模組

結合上述 2 種函數就能夠產生更好的函數,彌補 iconv_mime_decode() 的不足,並且能夠靈活地將字串轉為其他語系。

參考資料:

https://docs.python.org/3/library/email.html

https://docs.python.org/3/library/quopri.html

http://php.net/manual/en/function.iconv-mime-decode.php

http://php.net/manual/en/function.imap-mime-header-decode.php

FOLLOW US

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!