【问题标题】:Python line file iteration and strange charactersPython行文件迭代和奇怪的字符
【发布时间】:2010-04-29 13:57:43
【问题描述】:

我有一个巨大的压缩文本文件,我需要逐行阅读。我选择以下内容:

for i, line in enumerate(codecs.getreader('utf-8')(gzip.open('file.gz'))):
  print i, line

在文件后期的某个时间点,python 输出与文件不同。这是因为由于 python 认为是换行符的奇怪特殊字符而导致行被中断。当我在“vim”中打开文件时,它们是正确的,但是可疑字符的格式很奇怪。有什么办法可以解决这个问题吗?

我尝试过其他编解码器,包括 utf-16、latin-1。我也试过不使用编解码器。

我使用“od”查看了文件。果然,有 \n 字符不应该出现。但是,“错误”的前面有一个奇怪的字符。我认为这里有一些编码,一些字符是 2 字节,但如果没有正确查看,尾随字节是 \n。

根据“od -h file”,违规字符是“1d1c”。

如果我替换:

gzip.open('file.gz')

与:

os.popen('zcat file.gz')

它运行良好(实际上,速度更快)。但是,我想知道我哪里出错了。

【问题讨论】:

  • 在不知道有问题的线路是什么样子的情况下很难诊断。你能弄清楚它是什么行号,然后可以使用xxd 之类的东西在文件中发布原始行的十六进制转储(或者使用 vim,你可以使用 ctrl-a 查找字符的十六进制代码)
  • 向我们展示print repr(weird_special_characters) 的输出。当您在 vim 中打开文件时,什么是正确的?请比“格式奇怪”更精确。

标签: python gzip line-breaks codec


【解决方案1】:

不使用编解码器重试。以下重现了您使用编解码器时的问题,而没有它则没有问题:

import gzip 
import os 
import codecs 

data = gzip.open("file.gz", "wb") 
data.write('foo\x1d\x1cbar\nbaz') 
data.close() 

print list(codecs.getreader('utf-8')(gzip.open('file.gz'))) 
print list(os.popen('zcat file.gz')) 
print list(gzip.open('file.gz')) 

输出:

[u'foo\x1d', u'\x1c', u'bar\n', u'baz']
['foo\x1d\x1cbar\n', 'baz']
['foo\x1d\x1cbar\n', 'baz']

【讨论】:

    【解决方案2】:

    我问(在评论中)“”“向我们展示 print repr(weird_special_characters) 的输出。当你在 vim 中打开文件时,什么是正确的?请比“格式化奇怪”更精确。“”“但是什么都没有:-(

    您在查看od 的哪个文件? file.gz??如果您可以在其中看到任何可识别的内容,则它不是 gzip 文件!您没有看到换行符,您看到的是包含 0x0A 的二进制字节。

    如果原始文件是 utf-8 编码的,那么尝试使用其他编解码器有什么意义?

    “使用 zcat 可以正常工作”是否意味着您在没有 utf8 解码步骤的情况下获得了可识别的数据??

    我建议您简化您的代码,并一次执行一步...例如,请参阅this question 的已接受答案。再试一次,请显示您运行的确切代码,并在描述结果时使用 repr()。

    更新看来 DS 猜到了您试图解释的有关 \x1c 和 \x1d 的内容。

    这里有一些说明为什么会这样:

    在ASCII中,换行时只考虑\r和\n:

    >>> import pprint
    >>> text = ''.join('A' + chr(i) for i in range(32)) + 'BBB'
    >>> print repr(text)
    'A\x00A\x01A\x02A\x03A\x04A\x05A\x06A\x07A\x08A\tA\nA\x0bA\x0cA\rA\x0eA\x0fA\x10
    A\x11A\x12A\x13A\x14A\x15A\x16A\x17A\x18A\x19A\x1aA\x1bA\x1cA\x1dA\x1eA\x1fBBB'
    >>> pprint.pprint(text.splitlines(True))
    ['A\x00A\x01A\x02A\x03A\x04A\x05A\x06A\x07A\x08A\tA\n', # line break
     'A\x0bA\x0cA\r', # line break
     'A\x0eA\x0fA\x10A\x11A\x12A\x13A\x14A\x15A\x16A\x17A\x18A\x19A\x1aA\x1bA\x1cA\x
    1dA\x1eA\x1fBBB']
    >>>
    

    但是在 Unicode 中,字符 \x1D(文件分隔符)、\x1E(组分隔符)和 \x1E(记录分隔符)也可以作为行尾:

    >>> text = u''.join('A' + unichr(i) for i in range(32)) + u'BBB'
    >>> print repr(text)
    u'A\x00A\x01A\x02A\x03A\x04A\x05A\x06A\x07A\x08A\tA\nA\x0bA\x0cA\rA\x0eA\x0fA\x10A\x11A\x12A\x13A\x14A\x15A\x16A\x17A\x18A\x19A\x1aA\x1bA\x1cA\x1dA\x1eA\x1fBBB'
    >>> pprint.pprint(text.splitlines(True))
    [u'A\x00A\x01A\x02A\x03A\x04A\x05A\x06A\x07A\x08A\tA\n', # line break
     u'A\x0bA\x0cA\r', # line break
     u'A\x0eA\x0fA\x10A\x11A\x12A\x13A\x14A\x15A\x16A\x17A\x18A\x19A\x1aA\x1bA\x1c', # line break
     u'A\x1d', # line break
     u'A\x1e', # line break
     u'A\x1fBBB']
    >>>
    

    无论您使用什么编解码器,这都会发生。您仍然需要确定需要使用什么(如果有)编解码器。您还需要确定原始文件是否真的是文本文件而不是二进制文件。如果是文本文件,需要考虑文件中\x1c和\x1d的含义。

    【讨论】:

    • 我正在查看带有 od 的未压缩文件(特别是有问题的部分)。这就是'\n'出现的地方。我相信它实际上是一个 2 字节字符,当表示为 2 个单独的字节时,它具有“\n”。不幸的是我不知道原始编码是什么,但出于某种原因 vim 和 zcat 正确地表示它,这意味着它们正在检测它。
    • "未压缩文件"???您的代码没有显示您创建文件。请显示您实际运行的代码,该代码创建了您正在使用od 检查的文件。请显示来自 od 的十六进制转储,例如文件的前 100 个字节。请显示来自 od 的 100 字节的十六进制转储,以有问题的 `\n' 为中心。请通过编辑您的问题来展示所有这些。
    • 压缩文件为10GB。问题不在前 100 行,而是在接近 1Mth 行。我打印了 od 部分,这显然足以找出问题所在。我的代码没有解压缩代码,我是手动诊断的。我的代码没有创建文件,所以这也不相关。感谢您提供帮助,但您可以不那么好斗。
    猜你喜欢
    • 2013-11-17
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 2020-05-24
    • 2020-02-29
    • 2023-01-14
    • 2018-03-10
    • 1970-01-01
    相关资源
    最近更新 更多