一般處理電子郵件時,如果是全部都使用 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