【问题标题】:Reading File with Python 3.6使用 Python 3.6 读取文件
【发布时间】:2019-11-11 01:59:35
【问题描述】:

您好,我使用 Allen Downey 的 oreilly 书学习 Python3.x。在第 9 章中有一个例子来处理 Moby 项目文件中的单词列表。

https://en.wikipedia.org/wiki/Moby_Project

https://web.archive.org/web/20170930060409/http://icon.shef.ac.uk/Moby/

我使用以下 Python 行阅读了 German.txt 文件。

with open("german.txt") as log:
        for line in log:
                word = line.strip()
                if len(word) > 20:
                        print(word)


读了一些单词,但出现了中断,我得到了这些行。

Amtsueberschreitungen
Traceback (most recent call last):
  File "einlesen.py", line 8, in <module>
    for line in log:
  File "/home/alexander/anaconda3/lib/python3.6/codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 394: invalid start byte

符号是什么意思?如何使用 python 代码处理这个问题。

谢谢

【问题讨论】:

  • 您的文件采用哪种编码方式? Python 默认读取UTF-8。看起来,像一个编码问题。
  • @albert,显然该文件被编码为 Mac OS 罗马编码(见下面我的answer

标签: python python-3.x character-encoding


【解决方案1】:

根据documentation of open()

如果未指定编码,则使用的编码取决于平台:调用 locale.getpreferredencoding(False) 以获取当前的语言环境编码。

所以文件的读取方式因人而异。为了保证文件被正确读取您需要指定正确的编码

根据documentation of the Moby Project on Wikipedia,“保留了一些非 ASCII 重音字符,使用 Mac OS Roman 编码表示”。在documentation of the Python codecs module 中,您可以找到该编解码器的正确名称,即“mac_roman”。因此,您可以使用以下代码,这不会导致解码错误:

with open("german.txt", 'rt', encoding='mac_roman') as log:
    for line in log:
        word = line.strip()
        if len(word) > 20:
            print(word)

更新

尽管有文档,但该文件似乎并未使用 Mac OS 罗马编码进行编码。我使用all possible encodings 解码了文件并比较了结果。列表中只有 9 个非 ASCII 词,正如另一个答案中所指出的,“André”这个词似乎是正确的。以下是可能的编码列表(没有失败,包括单词“André”)和根据该编码解码的 9 个非 ASCII 单词:

encodings: cp437, cp860, cp861, cp863, cp865
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, p≥ange

encodings: cp720
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, pٌange

encodings: cp775
words: André, Attaché, Chāteau, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhōnetal, p“ange

encodings: cp850, cp858
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, p‗ange

encodings: cp852
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, p˛ange

对于上述所有编码,前 8 个字在解码时是相同的。仅对最后一个词有 9 种不同的结果。

基于此结果,我认为使用了 cp720 编码。但是,我不认识列表中的最后一个单词,所以我不能确定。由您决定哪种解码最适合您。

【讨论】:

  • +1 彻底,并展示了准确确定准确使用了哪种 8 位编码是多么困难。
【解决方案2】:

猜测文件的正确编码可能很棘手。让我们首先以二进制模式打开文件,找到有问题的字节,然后检查周围的字符。

>>> with open('german.txt', 'rb') as f:
...     bs = f.read()
... 
>>> bs.find(b'\x82')
24970
>>> bs[24960:24980]
b'nebel\rAndr\x82\rAndy\rAne'

所以字节 b'\x82' 是一个以 'Andr' 开头的五个字母单词中的最后一个字母。

this page 中查找 b'\x82'(由 Stack Overflow 用户 @tripleee 提供),我们可以看到它可能匹配的字符。我认为最有可能的匹配是“é”,给了我们正确的名字“André”。与list of Python encodings 进行交叉检查,最合适的编码是 cp850,这是西欧语言的传统编码。

此代码将正确读取文件:

>>> with open('german.txt', encoding='cp850') as f:
...     for line in f:
...         # do things with line

如果您在数据中发现任何“异常”字符,您可能需要尝试其他编码。这是因为 8 位编码很有可能成功解码一个字节,但结果毫无意义。例如,如果我们从 cp1252 解码:

>>> b'Andr\x82'.decode('cp1252')
'Andr‚'

【讨论】:

    【解决方案3】:

    根据我上面的评论,这看起来像是一个编码问题,经过测试,几乎就是这种情况。

    使用chardet module 检测编码给出:

    {'encoding': 'Windows-1252', 'confidence': 0.73, 'language': ''}
    

    这不是 Python 的默认 UTF8-encoding。为了读取其他编码的文件,需要在读取文件时通过open(filename, mode, encoding, ...)函数的encoding=参数指定所需的编码。

    由于事先可能不知道编码,因此使用chardet's UniversalDetector 确定文件编码然后将其传递给文件读取非常方便,如下所示:

    from chardet.universaldetector import UniversalDetector
    
    detector = UniversalDetector()
    detector.reset()
    with open('german.txt', 'rb') as file:
        for line in file:
            detector.feed(line)
            if detector.done:
                break
    detector.close()
    encoding = detector.result
    print(encoding)
    
    with open("german.txt", encoding=encoding) as log:
        for line_num, line in enumerate(log):
            word = line.strip()
            if len(word) > 20:
                print(line_num, word)
    

    注意:在我的机器上使用德语语言环境(MacOS 10.10.5 和 Python 3.6.2)可以正常工作,并且在检测到编码之前遇到了与 OP 一样的错误。我的地区是:

    LANG="de_DE.UTF-8"
    LC_COLLATE="de_DE.UTF-8"
    LC_CTYPE="de_DE.UTF-8"
    LC_MESSAGES="de_DE.UTF-8"
    LC_MONETARY="de_DE.UTF-8"
    LC_NUMERIC="de_DE.UTF-8"
    LC_TIME="de_DE.UTF-8"
    

    【讨论】:

    • +1 用于使用 chardet,尽管在这种情况下,我认为 chardet 对它的发现没有 100% 的信心是正确的。
    • 还在猜测中。由于 159809 个字中只有 9 个是非 ASCII 字,因此即使编码不正确,也很容易获得高置信度。如果您查看实际文件,您会发现使用 Windows-1252 对其进行解码将产生以下 9 个非 ASCII 字:Andr‚、Attach‚、Chƒteau、Conf‚rencier、C‚zanne、Faberg‚、L ‚vi-Strauss,Rh“netal,pòange。这对我来说似乎不正确!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-22
    • 2018-02-23
    • 1970-01-01
    • 2020-07-30
    • 1970-01-01
    • 2019-04-12
    • 1970-01-01
    相关资源
    最近更新 更多