Python解码对我有效但对其他人无效
我相信这个问题在某个地方已经被回答过,但我不知道该搜索什么。我的问题其实不是我自己的,而是其他人的。简单来说,我有一个Python脚本用于文本解码,对我来说解码没问题,但其他用户却失败了,即使他们用的是相同的代码和输入。
我写了一个脚本 (在Bitbucket上的源代码),这个脚本可以把Windows Mobile 6的短信(通过PIM Backup输出)转换成Android短信(通过SMS Backup & Restore输入),具体是把PIM Backup的内容转换成SMSB&R兼容的XML格式。
现在,PIM Backup输出的内容是用UCS-2小端格式,这个格式很好,因为它支持各种国际交流。在我的脚本中,我使用Python内置的字符串解码来加载内容,并创建一个csv读取对象,代码如下:
# Read the file contents
sms_text = csv_file.read().decode('utf-16').split(os.linesep)
sms_reader = csv.reader(sms_text, delimiter=';', quotechar='"', escapechar='\\')
然后我用以下代码处理csv读取器的每一行:
row = sms_reader.next()
我把这个放在一个try
块里,因为偶尔会抛出UnicodeEncodeError
,当某些东西不太对劲时。但对我来说,这种情况非常少见。
我的问题是,其他用户在使用非ASCII字符的短信时,这个错误几乎总是会出现。最近有个德国用户联系我,说他只有大约10%的短信能正确解码。他把他的.pib文件发给我,我用我的脚本处理了一遍,结果没有遇到任何问题。所有输出看起来都是标准的ANSI/ISO 8859-1/Windows-1252等,所以并不复杂。
我想知道,为什么这些用户在解码输入时会失败,而我却没有问题,使用的代码(和Python版本)完全一样?作为后续问题,我该如何修改我的脚本,让它对所有人都有效?
编辑:我忘记提一个重要的点,我是在Eclipse中使用PyDev运行这个脚本的。当我在命令提示符下运行时,遇到的所有问题和其他人一样!我仍然不知道问题出在哪里,但希望这能帮助缩小范围。
以下是一个非常简单的.csm文件示例(从.pib文件提取,名字和号码已更改),里面有非标准字符:
Msg Id;Sender Name;Sender Address;Sender AddressType;Prefix;Subject;Body;BodyType;Folder;Account;Msg Class;Content Length;Msg Size;Msg Flags;Msg Status;Modify Time;Delivery Time;Recipient Nbr;Recipients;Attachment Nbr;Attachments
0x00,0x00;"491703000000";"491703000000";;"";"Wir wünschen dem rainer alles gute und viel gesundheit! Bis nächste woche, wir hören uns bis dahin noch mal.. Liebe grüße aus md!";"";0;"\\%MDF3";"SMS";"IPM.SMStext";;;33;262144;2007,09,23,19,44,32;2007,09,23,19,44,31;1;"851980\;Gela\;+491739000000\;1\;0\;SMS";0;""
不过,仅仅通过处理这个字符串很难准确找出问题,因为我自己没有遇到这个异常。
还有一个我确实遇到问题的例子(即使在Eclipse中)是:
Msg Id;Sender Name;Sender Address;Sender AddressType;Prefix;Subject;Body;BodyType;Folder;Account;Msg Class;Content Length;Msg Size;Msg Flags;Msg Status;Modify Time;Delivery Time;Recipient Nbr;Recipients;Attachment Nbr;Attachments
0x00,0x00;"Jonas/M";"\"Jonas/M\" <+46737000000>";;"";"Den går 28 ";"";2;"\\%MDF4";"SMS";"IPM.SMStext";0;24;0;0;2011,03,12,21,15,19;2011,03,12,21,16,17;0;"";0;""
0x00,0x00;"Don Vär";"\"Don Vär\" <+46709000000>";;"";"försöke® dhdjhdhhdjehdejehţýùhbfvfghjujhuikjkłánjajnxsjajmsxnsmajmkjsnshdjnsjmwkjhdnjsjmwkjdhjjdewjjwjwjw®";"";2;"\\%MDF1";"SMS";"IPM.SMStext";0;212;1;0;2010,05,17,15,56,49;2010,05,17,15,55,46;0;"";0;""
异常追踪信息是:
Traceback (most recent call last):
File "C:\Programming\workspace\pim2smsbr\src\pim2smsbr.py", line 207, in <module>
convert(args.source[0], args.out)
File "C:\Programming\workspace\pim2smsbr\src\pim2smsbr.py", line 98, in convert
row = sms_reader.next()
File "C:\Python27\lib\encodings\cp1252.py", line 12, in encode
return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character u'\ue403' in position 77: character maps to <undefined>
更新:
John Machin的回答非常有效。我只改了一行,问题就解决了。把:
sms_text = csv_file.read().decode('utf-16').split(os.linesep)
改为:
sms_text = csv_file.read().decode('utf-16').encode('utf-8').splitlines()
1 个回答
你可以先给我们一个你能读取的PIM备份文件的样本,而德国用户却无法读取的那个。
你偶尔会遇到UnicodeEncodeError(注意是编码错误,不是解码错误),这很重要。能不能把你的代码改一下,让它显示出确切的错误信息和错误追踪,而不是把它们隐藏起来?
你是在Linux、OSX还是Windows上运行这个?如果是在Windows上,是在命令提示符窗口里吗?如果是的话,CHCP命令给你什么信息?它给你的德国朋友又是什么信息呢?
你有没有看过csv文档里关于Unicode的说明?事情是这样的:
>>> import csv
>>> r = csv.reader([u"\xA0"])
>>> r.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 0: ordinal not in range(128)
>>>
如果你按照以下步骤操作,成功的机会会大大增加:
- 读取文件中的原始字节
- 使用UTF-16将字节串解码为Unicode
- 将Unicode字符串编码为UTF-8
- 把UTF-8字符串分割成行的列表(使用
str.splitlines()
) - 从这个列表中创建一个csv读取器
- 遍历每一行,将每个单元格从UTF-8解码为Unicode。
更新 我在你对问题的编辑中没有看到任何让我改变之前建议的内容。你可以选择省略上面第6步(这样做虽然可行,但不太好)或者包括第6步,并重写你的输出阶段,使用[c]ElementTree
或lxml
来进行UTF-8编码、转义等。顺便提一下,你正在写的XML文件声称它们是用UTF-8编码的。我无法重现这个问题,因为我没有Eclipse,但我怀疑你在Eclipse下运行时写的“OK”的XML文件实际上是用cp1252
编码的。你试过用XML验证器检查它们吗?
你遇到的U+E403字符只是你脚本只能“工作”于csv模块在面对unicode
输入时选择的编码的一部分问题。这个字符在某个专用用户区域(PUA)块中,是为特定厂商(比如苹果符号)或应用程序保留的。它不在Python提供的编码范围内,无法正确显示(因为它不在已发布的字体中)。搜索“emoji E403”并跟踪结果显示,它可能是Unicode 6.0中新加入的U+1F614沉思脸。