在Python中读取不同编码文件后的输出差异

3 投票
1 回答
1172 浏览
提问于 2025-04-17 21:05

我有一个用utf-8编码的unicode字符串列表文件,还有一个用普通ansi编码的输入文件。我从这个ansi文件中读取目录路径,然后使用os.walk()来查找是否有文件在列表中(这个列表是用utf-8保存的)。但是即使文件确实存在,它们也没有匹配上。

后来,我用一个单独的字符串“40M_Ãz­µ´ú¸ÕÀÉ”进行了简单的检查,并把这个字符串(从记事本中复制的)分别保存到三个不同的文件中,编码选项分别是ansi、unicode和utf-8。我写了一个python脚本来打印:

print repr(string)
print string

输出结果是这样的:

ANSI编码

'40M_\xc3z\xad\xb5\xb4\xfa\xb8\xd5\xc0\xc9'
40M_Ãz­µ´ú¸ÕÀÉ

UNICODE编码

'\x004\x000\x00M\x00_\x00\xc3\x00z\x00\xad\x00\xb5\x00\xb4\x00\xfa\x00\xb8\x00\xd5\x00\xc0\x00\xc9\x00'
 4 0 M _ Ã z ­µ ´ ú ¸ Õ À É

UTF-8编码

'40M_\xc3\x83z\xc2\xad\xc2\xb5\xc2\xb4\xc3\xba\xc2\xb8\xc3\x95\xc3\x80\xc3\x89'
40M_Ãz­µ´ú¸ÕÀÉ

我真的不明白如何比较来自不同编码文件的相同字符串。请帮帮我。

附言:我有一些典型的unicode字符,比如:唐朝小栗子第集.mp3,这些字符很难处理。

1 个回答

3

我真的不明白如何比较来自不同编码文件的相同字符串。

记事本用三种不同的编码方式对你的字符字符串进行了编码,结果产生了三种不同的字节序列。要获取字符字符串,你必须使用相同的编码来解码这些字节:

>>> ansi_bytes  = '40M_\xc3z\xad\xb5\xb4\xfa\xb8\xd5\xc0\xc9'
>>> utf16_bytes = '4\x000\x00M\x00_\x00\xc3\x00z\x00\xad\x00\xb5\x00\xb4\x00\xfa\x00\xb8\x00\xd5\x00\xc0\x00\xc9\x00'
>>> utf8_bytes  = '40M_\xc3\x83z\xc2\xad\xc2\xb5\xc2\xb4\xc3\xba\xc2\xb8\xc3\x95\xc3\x80\xc3\x89'

>>> ansi_bytes.decode('mbcs')
u'40M_\xc3z\xad\xb5\xb4\xfa\xb8\xd5\xc0\xc9' # 40M_Ãz­µ´ú¸ÕÀÉ
>>> utf16_bytes.decode('utf-16le')
u'40M_\xc3z\xad\xb5\xb4\xfa\xb8\xd5\xc0\xc9' # 40M_Ãz­µ´ú¸ÕÀÉ
>>> utf8_bytes.decode('utf-8')
u'40M_\xc3z\xad\xb5\xb4\xfa\xb8\xd5\xc0\xc9' # 40M_Ãz­µ´ú¸ÕÀÉ
  • ‘ANSI’(不是“ASCI”)是Windows(有点误导)称其默认本地特定代码页的名称,在你的情况下是1252(西欧编码,你可以在Python中用windows-1252获取),但这会因机器而异。你可以在Windows的Python中使用名称mbcs来获取这种编码。

  • ‘Unicode’是Windows用来表示UTF-16LE编码的名称(这也很误导,因为Unicode是字符集标准,而不是任何字节和字符之间的编码)。与ANSI和UTF-8不同,这不是一种兼容ASCII的编码,所以你尝试从文件中读取一行时失败了,因为UTF-16LE的行结束符不是\n,而是\n\x00。这导致你上面的字节字符串开头多了一个\x00

  • ‘UTF-8’这个名字至少是准确的,但Windows喜欢在其“UTF-8”文件的开头放置假字节顺序标记,这会在解码时给你一个不想要的u'\uFEFF'字符。如果你想接受从记事本保存的“UTF-8”文件,你可以手动去掉这个标记,或者使用Python的utf-8-sig编码。

你可以使用codecs.open()代替open()来读取文件,这样可以自动进行Unicode解码。这也解决了UTF-16换行符的问题,因为这样\n字符是在解码后被检测到的,而不是之前。

我从那个ASCII文件中读取目录路径,然后执行os.walk()

Windows文件名本质上是以Unicode处理的,所以当你给Windows一个字节字符串时,它必须猜测需要什么编码来将这些字节转换为字符。它选择ANSI而不是UTF-8。如果你使用的字节字符串也是用同一台机器的ANSI编码,那么这没问题。然而,在这种情况下,你只能使用符合你机器本地编码的文件名。在西欧编码中,40M_Ãz­µ´ú¸ÕÀÉ是可以的,但唐朝小栗子第集.mp3就不行,所以你根本无法引用中文文件。

Python支持直接将Unicode文件名传递给Windows,这样就避免了这个问题(大多数其他语言做不到这一点)。将Unicode字符串传递给文件系统函数,比如os.walk(),你应该能得到Unicode字符串,而不是失败。

所以,对于UTF-8编码的输入文件,可以这样做:

with codecs.open(u'directory_path.txt', 'rb', 'utf-8-sig') as fp:
    directory_path = fp.readline().strip(u'\r\n') # unicode dir path

good_names = set()
with codecs.open(u'filename_list.txt', 'rb', 'utf-8-sig') as fp:
    for line in fp:
        good_names.add(line.strip(u'\r\n')) # set of unicode file names

for dirpath, dirnames, filenames in os.walk(directory path): # names will be unicode strings
    for filename in filenames:
        if filename in good_names:
            # do something with file

撰写回答