Python 行文件迭代与奇怪字符

3 投票
2 回答
2042 浏览
提问于 2025-04-15 22:09

我有一个很大的gzipped文本文件,需要逐行读取。我用的是下面的代码:

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')

这样就能正常工作了(而且速度还快)。不过,我想知道我哪里出错了。

2 个回答

1

我在评论里问过:“给我们看看 print repr(weird_special_characters) 的输出。你在 vim 中打开文件时,什么是正确的?请比‘格式奇怪’更具体一些。”但是没有得到任何回复 :-(

你用 od 查看的是哪个文件?是 file.gz 吗?如果你在里面看到任何可以识别的内容,那就不是 gzip 文件!你看到的不是换行符,而是包含 0x0A 的二进制字节。

如果原始文件是 utf-8 编码的,那你为什么还要尝试其他编码呢?

“与 zcat 一起工作正常”是说你在没有 utf8 解码步骤的情况下得到了可识别的数据吗?

我建议你简化你的代码,一步一步来……比如看看这个问题的接受答案:这个问题。再试一次,并请展示你运行的确切代码,描述结果时使用 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 的含义。

5

再试一次,不要使用编解码器。下面的内容展示了当使用编解码器时会出现的问题,而不使用它时就不会有这个问题:

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']

撰写回答