【问题标题】:Parse file in robust way with python 3使用 python 3 以稳健的方式解析文件
【发布时间】:2014-07-14 16:22:48
【问题描述】:

我有一个需要逐行查看的日志文件,显然它包含一些“坏字节”。我收到一条错误消息,内容如下:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 9: invalid start byte

我已经能够将问题简化为包含以下行的文件“log.test”:

Message: \260

(至少它在我的 Emacs 中是这样显示的。)

我有一个文件“demo_error.py”,看起来像这样:

import sys
with open(sys.argv[1], 'r') as lf:
    for i, l in enumerate(lf):
        print(i, l.strip())

然后我从命令行运行:

$ python3 demo_error.py log.test

完整的回溯是:

Traceback (most recent call last):
  File "demo_error.py", line 5, in <module>
    for i, l in enumerate(lf):
  File     "/usr/local/Cellar/python3/3.4.0/Frameworks/Python.framework/Versions/3.4/lib/python3.4/codecs.py", line 313, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 13: invalid start byte

我的直觉是我必须以某种方式指定一个更通用的编解码器(例如“原始 ascii”) - 但我不太确定如何做到这一点。

请注意,这在 Python 2.7 中并不是真正的问题。

为了明确我的观点:我不介意为有问题的行获得例外 - 然后我可以简单地丢弃该行。问题是异常似乎发生在“for”循环本身,这使得对该特定行的特殊处理变得不可能。

【问题讨论】:

    标签: python python-3.x


    【解决方案1】:

    您也可以使用codecs 模块。当您使用 codecs.open() 函数时,您可以使用 errors 参数指定它如何处理错误:

    codecs.open(filename, mode[, encoding[, errors[, buffering]]])
    

    errors 参数可以是几个不同的关键字之一,这些关键字指定当 Python 尝试解码对当前编码无效的字符时,您希望它的行为方式。您可能对 codecs.ignore_errorscodecs.replace_errors 最感兴趣,它们会分别导致无效字符被忽略或替换为默认字符。

    当您知道您有损坏的数据时,即使您指定了正确的编码,也会导致引发 UnicodeDecodeError,此方法可能是一个不错的选择。

    例子:

    with codecs.open('file.txt', mode='r', errors='ignore'):
        # ...stuff...
        # Even if there is corrupt data and invalid characters for the default
        # encoding, this open() will still succeed
    

    【讨论】:

    • 使用'codecs'的好处是代码可以兼容Python 2.x。
    【解决方案2】:

    显然您的文件不包含有效的 UTF-8(这是默认编码)。 如果您知道使用什么编码(例如 iso-8859-1,它是 python2 默认的 afaik),您可以在打开时指定它使用

    open(sys.argv[1], mode='r', encoding='iso-8859-1')
    

    如果编码未知或全部无效,您可以将文件作为二进制文件打开。

    open(sys.argv[1], mode='rb')
    

    这将使内容可以作为字节访问,而不是试图将它们解释为字符。

    【讨论】:

    • 指定编码可以解决问题,谢谢。以二进制形式打开也可以,但是它使后处理变得更加棘手,因为大部分行都包含有效文本。 (事实上​​,我相信它应该包含 ascii ......但是它已经损坏了。)
    • 0x0b 也不是有效的 ascii。然而,它是有效的 iso-8859-1,表示度数符号 (°)。如果文件实际上已损坏,则使用二进制文件会更可靠,因为某些字节可能不是有效的 iso-8859-1。
    • Python 默认编码不一定是 UTF-8。根据Python 3.4 Standard Library documentation默认编码取决于平台(无论 locale.getpreferredencoding() 返回什么)
    【解决方案3】:

    在 python

    在 python >=3 中,字符串是 unicode 字符串(每个字符 16 位)。因此,当读取文件时,python 必须对文件进行解码,并且默认情况下它使用系统编码 - 不一定是 UTF-8。在你的情况下,它似乎假设 UTF-8 编码,当你的日志文件不是 UTF-8 编码所以例外。

    如果不确定编码,您可以合理地尝试使用 ISO-8859-1 和

    open(sys.argv[1], 'r', encoding='iso-8859-1')
    

    【讨论】:

      猜你喜欢
      • 2018-05-18
      • 2013-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-23
      • 2019-03-23
      • 1970-01-01
      • 2014-09-30
      相关资源
      最近更新 更多