如何解码这个看似GBK编码的字符串?

Nic*_*oul 1 email attachments character-encoding chinese decoding

我有一个.eml包含 MS-Word 附件的电子邮件文件:

------=_Part_239376_662463351.1415605722579
Content-Type: application/msword;
 name="=?GBK?B?1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo?=
 =?GBK?B?sai1xLTwuLQoz8K6vszBMbrFKS5kb2M=?="
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="=?GBK?B?1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo?=
 =?GBK?B?sai1xLTwuLQoz8K6vszBMbrFKS5kb2M=?="

0M8R4KGxGuEAAAAAAAAA [rest of base64-encoded attachment]
Run Code Online (Sandbox Code Playgroud)

附件base64解码成功,文件内容正常。
但是如何解码文件名呢?

的值filename="" 似乎GBK编码的,但 Python.decode('gbk')对它不起作用,返回相同的字符串:

>>> "1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo".decode('gbk')
u'1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo'
Run Code Online (Sandbox Code Playgroud)

那么,这个字符串是用什么编码的以及如何解码呢?

=?GBK?B?1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo?=
=?GBK?B?sai1xLTwuLQoz8K6vszBMbrFKS5kb2M=?=
Run Code Online (Sandbox Code Playgroud)

use*_*686 5

这些 -

=?GBK?B?1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo?=
=?GBK?B?sai1xLTwuLQoz8K6vszBMbrFKS5kb2M=?=
Run Code Online (Sandbox Code Playgroud)

– 是 MIME编码字。一般形式为:

=?字符集编码编码文本?=

您认为字符集是 GBK 是正确的,但您必须首先解释传输编码,该编码B适用于 Base64 或QQuoted-Printable。因此:

py3.5 >>> base64.b64decode("sai1xLTwuLQoz8K6vszBMbrFKS5kb2M=").decode("GBK")
'????(???1?).doc'
Run Code Online (Sandbox Code Playgroud)

但是,email.header会更好地处理这个问题:

py3.5 >>> email.header.decode_header("=?GBK?B?1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo?= =?GBK?B?sai1xLTwuLQoz8K6vszBMbrFKS5kb2M=?=")
[(b'\xd5\xc2\xb9\xb1\xc7\xf8\xb3\xc7\xb9\xdc\xbe\xd6\xb9\xd8\xd3\xda\xcd\xf8\xc2\xe7\xd3\xdf\xc7\xe9\xd0\xc5\xcf\xa2\xd7\xa8\xb1\xa8\xb5\xc4\xb4\xf0\xb8\xb4(\xcf\xc2\xba\xbe\xcc\xc11\xba\xc5).doc', 'gbk')]
py3.5 >>> _[0][0].decode(_[0][1])
'???????????????????(???1?).doc'
Run Code Online (Sandbox Code Playgroud)

第一个结果的结构是这样的,因为单个标题可能有多个组件,即不同的编码或混合原始文本和编码字。与 Perl 的 Encode 不同,Python 模块让您自行决定 join() 结果:

def decode_header(enc):
    dec = email.header.decode_header(enc)
    dec = [f[0].decode(f[1] or "us-ascii") for f in dec]
    return "".join(dec)
Run Code Online (Sandbox Code Playgroud)

说到 Perl:

$ perl -E 'use open qw(:std :utf8);
           use Encode;
           say Encode::decode("MIME-Header", "=?GBK?B?1cK5scf4s8e53L7WudjT2s34wufT38fp0MXPoteo?= =?GBK?B?sai1xLTwuLQoz8K6vszBMbrFKS5kb2M=?=");'
???????????????????(???1?).doc
Run Code Online (Sandbox Code Playgroud)

(此外,正文不是 uuencoded,而是Base64编码的。它们使用不同的字符集,即使两者都是 3:4 编码并且uudecode通常足够智能以检测原始 Base64。)