如何让 email.Header.decode_header 与 non-ASCII 个字符一起工作?
How to get email.Header.decode_header to work with non-ASCII characters?
我借用了下面的代码来解析电子邮件 headers,另外还添加了一个 header。无可否认,我不完全理解围绕 email.Headers
模块应该直接使用的所有脚手架的原因。
值得注意的是Headers
没有实例化;而它的 decode_header
函数被称为:
class DecodedHeader(object):
def __init__(self, s, folder):
self.msg=email.message_from_string(s[1])
self.info=parseList(s[0])
self.folder=folder
def __getitem__(self,name):
if name.lower()=='folder': return self.folder
elif name.lower()=='uid': return self.info[1][3]
elif name.lower()=='flags': return ','.join(self.info[1][1])
elif name.lower()=='internal-date':
ds= self.info[1][5]
if Options.dateFormat:
ds= time.strftime(Options.dateFormat,imaplib.Internaldate2tuple('INTERNALDATE "'+ds+'"'))
return ds
elif name.lower()=='size': return self.info[1][7]
val= self.msg.__getitem__(name)
if val==None: return None
return self._convert(email.Header.decode_header(val),name)
def get(self,key,default=None):
return self.__getitem__(key)
def _convert(self, list, name):
l=[]
for s, encoding in list:
try:
if (encoding!=None):
s=unicode(s,encoding, 'replace').encode(Options.encoding,'replace')
except Exception, e:
print >>sys.stderr, "Encoding error", e
l.append(s)
res= "".join(l)
if Options.addr and name.lower() in ('from','to', 'cc', 'return-path','reply-to' ): res=self._modifyAddr(res)
if Options.dateFormat and name.lower() in ('date'): res = self._formatDate(res)
return res
问题是:当 header (val) 包含 non-ASCII 个字符,例如 Ä 和 ä,我得到:
Traceback (most recent call last):
File "v12.py", line 434, in <module>
main()
File "v12.py", line 396, in main
writer.writerow(msg)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/csv.py", line 152, in writerow
return self.writer.writerow(self._dict_to_list(rowdict))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/csv.py", line 149, in _dict_to_list
return [rowdict.get(key, self.restval) for key in self.fieldnames]
File "v12.py", line 198, in get
return self.__getitem__(key)
File "v12.py", line 196, in __getitem__
return self._convert(email.Header.decode_header(val),name)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/email/header.py", line 76, in decode_header
header = str(header)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 1: ordinal not in range(128)
其中 u'\xe4' 是 ä.
我已经尝试了一些方法:
- 将#-- 编码:utf-8 -- 添加到header.py
的顶部
- 在将
val
传递给 decode_header()
之前调用 unicode()
- 在将
val
传递给 decode_header()
之前调用 .encode('utf-8')
- 在将
val
传递给 decode_header()
之前调用 .encode('ISO-8859-1')
对以上任何一项都不满意。这是什么原因?鉴于我希望保持 email.Header
的用法如上(Header 不是 直接实例化),我们如何确保 non-ASCII字符被 decode_header
?
成功解码
header 必须正确编码才能解码。看起来 val
来自一个已经存在的消息,所以该消息可能是错误的。该错误表明它是一个 Unicode 字符串,但此时它应该是一个字节字符串。 email.header Python 帮助中的示例很简单。
下面对两个甚至不使用相同编码的 header 进行编码:
>>> import email.header
>>> h = email.header.Header(u'To: Märk'.encode('iso-8859-1'),'iso-8859-1')
>>> h.append(u'From: Jòhñ'.encode('utf8'),'utf8')
>>> h
<email.header.Header instance at 0x00559F58>
>>> s = h.encode()
>>> s
'=?iso-8859-1?q?To=3A_M=E4rk?= =?utf-8?b?RnJvbTogSsOyaMOx?='
请注意,正确编码的 header 是一个嵌入了编码名称的字节字符串,它不使用 non-ASCII 个字符。
解码它们:
>>> email.header.decode_header(s)
[('To: M\xe4rk', 'iso-8859-1'), ('From: J\xc3\xb2h\xc3\xb1', 'utf-8')]
>>> d = email.header.decode_header(s)
>>> for s,e in d:
... print s.decode(e)
...
To: Märk
From: Jòhñ
我借用了下面的代码来解析电子邮件 headers,另外还添加了一个 header。无可否认,我不完全理解围绕 email.Headers
模块应该直接使用的所有脚手架的原因。
值得注意的是Headers
没有实例化;而它的 decode_header
函数被称为:
class DecodedHeader(object):
def __init__(self, s, folder):
self.msg=email.message_from_string(s[1])
self.info=parseList(s[0])
self.folder=folder
def __getitem__(self,name):
if name.lower()=='folder': return self.folder
elif name.lower()=='uid': return self.info[1][3]
elif name.lower()=='flags': return ','.join(self.info[1][1])
elif name.lower()=='internal-date':
ds= self.info[1][5]
if Options.dateFormat:
ds= time.strftime(Options.dateFormat,imaplib.Internaldate2tuple('INTERNALDATE "'+ds+'"'))
return ds
elif name.lower()=='size': return self.info[1][7]
val= self.msg.__getitem__(name)
if val==None: return None
return self._convert(email.Header.decode_header(val),name)
def get(self,key,default=None):
return self.__getitem__(key)
def _convert(self, list, name):
l=[]
for s, encoding in list:
try:
if (encoding!=None):
s=unicode(s,encoding, 'replace').encode(Options.encoding,'replace')
except Exception, e:
print >>sys.stderr, "Encoding error", e
l.append(s)
res= "".join(l)
if Options.addr and name.lower() in ('from','to', 'cc', 'return-path','reply-to' ): res=self._modifyAddr(res)
if Options.dateFormat and name.lower() in ('date'): res = self._formatDate(res)
return res
问题是:当 header (val) 包含 non-ASCII 个字符,例如 Ä 和 ä,我得到:
Traceback (most recent call last):
File "v12.py", line 434, in <module>
main()
File "v12.py", line 396, in main
writer.writerow(msg)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/csv.py", line 152, in writerow
return self.writer.writerow(self._dict_to_list(rowdict))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/csv.py", line 149, in _dict_to_list
return [rowdict.get(key, self.restval) for key in self.fieldnames]
File "v12.py", line 198, in get
return self.__getitem__(key)
File "v12.py", line 196, in __getitem__
return self._convert(email.Header.decode_header(val),name)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/email/header.py", line 76, in decode_header
header = str(header)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 1: ordinal not in range(128)
其中 u'\xe4' 是 ä.
我已经尝试了一些方法:
- 将#-- 编码:utf-8 -- 添加到header.py 的顶部
- 在将
val
传递给decode_header()
之前调用 unicode()
- 在将
val
传递给decode_header()
之前调用 .encode('utf-8')
- 在将
val
传递给decode_header()
之前调用 .encode('ISO-8859-1')
对以上任何一项都不满意。这是什么原因?鉴于我希望保持 email.Header
的用法如上(Header 不是 直接实例化),我们如何确保 non-ASCII字符被 decode_header
?
header 必须正确编码才能解码。看起来 val
来自一个已经存在的消息,所以该消息可能是错误的。该错误表明它是一个 Unicode 字符串,但此时它应该是一个字节字符串。 email.header Python 帮助中的示例很简单。
下面对两个甚至不使用相同编码的 header 进行编码:
>>> import email.header
>>> h = email.header.Header(u'To: Märk'.encode('iso-8859-1'),'iso-8859-1')
>>> h.append(u'From: Jòhñ'.encode('utf8'),'utf8')
>>> h
<email.header.Header instance at 0x00559F58>
>>> s = h.encode()
>>> s
'=?iso-8859-1?q?To=3A_M=E4rk?= =?utf-8?b?RnJvbTogSsOyaMOx?='
请注意,正确编码的 header 是一个嵌入了编码名称的字节字符串,它不使用 non-ASCII 个字符。
解码它们:
>>> email.header.decode_header(s)
[('To: M\xe4rk', 'iso-8859-1'), ('From: J\xc3\xb2h\xc3\xb1', 'utf-8')]
>>> d = email.header.decode_header(s)
>>> for s,e in d:
... print s.decode(e)
...
To: Märk
From: Jòhñ