使用Python和fileinput处理Unicode文件

9 投票
1 回答
12278 浏览
提问于 2025-04-18 13:23

我越来越觉得,文件编码这事儿被搞得特别复杂,简直是故意的。我遇到了一个问题,就是读取一个用utf-8编码的文件,这个文件就只有一行:

“blabla this is some text”

(注意,这里的引号是一些花哨版的标准引号。)

现在,我在这个文件上运行了一段Python代码:

import fileinput
def charinput(paths):
    with open(paths) as fi:
        for line in fi:
            for char in line:
                yield char
i = charinput('path/to/file.txt')
for item in i:
    print(item)

结果有两个:如果我从命令提示符运行我的Python代码,结果是一些奇怪的字符,后面跟着一个错误信息:

ď
»
ż
â
Traceback (most recent call last):
  File "krneki.py", line 11, in <module>
    print(item)
  File "C:\Python34\lib\encodings\cp852.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_map)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u20ac' in position
0: character maps to <undefined>

我觉得问题出在Python试图读取一个“错误”编码的文档,但有没有办法让fileinput.input读取utf-8呢?


补充:一些真的很奇怪的事情发生了,我对这些完全没有头绪。在用notepad++保存同样的文件后,Python代码现在在IDLE中运行,结果如下(换行符被去掉了):

“blabla this is some text”

而如果我先输入chcp 65001,命令提示符就不会崩溃。然后运行文件的结果是:

Ä»żâ€śblabla this is some text ”

有什么想法吗?我觉得这真是一团糟,但我必须搞明白……

1 个回答

14

编码

每个文件都有编码。比如,字节0x4C在ASCII编码下被解释为大写字母L,而在EBCDIC编码下则被解释为小于号('<')。没有所谓的纯文本。

有一些单字节字符集,比如ASCII,它用一个字节来编码每个符号;还有一些双字节字符集,比如KS X 1001,它用两个字节来编码每个符号;还有像流行的UTF-8这样的编码,它为每个符号使用可变数量的字节。

UTF-8已经成为新应用中最流行的编码方式,举几个例子:拉丁大写字母A用一个字节存储:0x41左双引号(“)用三个字节存储:0xE2 0x80 0x9C。而表情符号便便则用四个字节存储:0xF0 0x9F 0x92 0xA9

任何读取文件并需要将字节解释为符号的程序,都必须知道(或猜测)使用了哪种编码。

如果你对Unicode或UTF-8不太了解,可以看看这篇文章

在Python 3中读取文件

Python 3的内置函数open()有一个可选的关键字参数encoding,用来支持不同的编码。要打开一个UTF-8编码的文件,你可以写open(filename, encoding="utf-8"),Python会自动处理解码。

此外,fileinput模块也支持通过openhook关键字参数来处理编码:fileinput.input(filename, openhook=fileinput.hook_encoded("utf-8"))

如果你对Python和Unicode或UTF-8不太熟悉,建议你阅读这篇文档,我还发现了一些不错的技巧在这里

在Python 2中读取字符串

在Python 2中,open()并不知道编码的事情。你可以使用codecs模块来指定使用的编码:codecs.open(filename, encoding="utf-8")

关于Python 2和Unicode的最佳资料是这篇文档

撰写回答