1

我有一个保存为 csv 文件的特征向量的巨大文件(1.2GB)。为了通过这些行,我创建了一个 python 类,它一次将一批行从巨型文件加载到内存中。

为了让这个类知道在文件中读取的确切位置以获得一批batch_size完整的行(比如说batch_size=10,000),在第一次使用一个巨大的文件时,这个类会遍历整个文件一次,并注册每行的偏移量,并将这些偏移量保存到帮助文件中,以便稍后它可以“file.seek(starting_offset); batch = file.read(num_bytes)”读取下一批行。

首先,我以这种方式实现了线偏移的注册:

    offset = 0;
    line_offsets = [];
    for line in self.fid:
        line_offsets.append(offset);
        offset += len(line); 

它与 Giant_file1 一起工作得很好。

但后来我处理了这些特征并创建了giant_file2(具有标准化特征),在我制作的这个类的帮助下。接下来,当我想从 Giant_file2 中读取成批的行时,它失败了,因为它将读取的批处理字符串不在正确的位置(例如,读取类似“-00\n15.467e-04,... " 而不是 "15.467e-04,...\n")。

所以我尝试将线偏移计算部分更改为:

    offset = 0;
    line_offsets = [];
    while True:
        line = self.fid.readline();

        if (len(line) <= 0):
            break;

        line_offsets.append(offset);
        offset = self.fid.tell();

主要变化是我注册的偏移量取自 fid.tell() 的结果,而不是行的累积长度。

这个版本在 Giant_file2 上运行良好,但在 Giant_file1 上失败了。

深入研究后,我发现函数 seek()、tell() 和 read() 彼此不一致。例如:

fid = file('giant_file1.csv');
fid.readline();
>>>'0.089,169.039,10.375,-30.838,59.171,-50.867,13.968,1.599,-26.718,0.507,-8.967,-8.736,\n'
fid.tell();
>>>67L
fid.readline();
>>>'15.375,91.43,15.754,-147.691,54.234,54.478,-0.435,32.364,4.64,29.479,4.835,-16.697,\n'
fid.seek(67);
fid.tell();
>>>67L
fid.readline();
>>>'507,-8.967,-8.736,\n'

这里有一些矛盾:当我定位(根据 fid.tell())在字节 67 时,一旦读取的行是一回事,而在第二次(再次当 fid.tell() 报告时,我定位在字节67) 读取的行不同。

我不能相信 tell() 和 seek() 将我放在所需位置以从所需行的开头读取。另一方面,当我使用(使用 Giant_file1)字符串长度作为 seek() 的参考时,我得到了正确的位置:

fid.seek(0);
line = fid.readline();
fid.tell();
>>>87L
len(line);
>>>86
fid.seek(86);
fid.readline();
>>>'15.375,91.43,15.754,-147.691,54.234,54.478,-0.435,32.364,4.64,29.479,4.835,-16.697,\n'

那么发生了什么?

我能想到的giant_file1 和giant_file2 之间的唯一区别是giant_file1 中的值是用小数点写的(例如-0.435),而在giant_file2 中它们都是科学格式(例如-4.350e-01)。我不认为它们中的任何一个都是用 unicode 编码的(我认为是这样,因为我用简单的 file.read() 读取的字符串似乎是可读的。我怎样才能确定?)。

非常感谢您的帮助,包括解释、原因的想法以及可能的解决方案(或解决方法)。

谢谢你,约纳坦。

4

2 回答 2

2

我认为你有一个换行问题。检查giant_file1.csv 是以\n 或\r\n 结尾的行如果您以文本模式打开文件,文件将返回以\n 结尾的行,并且丢弃多余的\r。因此,当您查看返回的行的长度时,它将与实际文件位置相差 1(这不仅消耗了 \n,而且还消耗了 \r\n)。当然,这些错误会随着您阅读更多行而累积。

解决方案是以二进制模式打开文件,而不是。在这种模式下,没有 \r\n -> \n 减少,因此您的行长计数将与您的文件 tell( ) 查询保持一致。

我希望这可以为您解决 - 因为它很容易解决。:) 祝你的项目好运,编码愉快!

于 2012-06-14T16:19:17.253 回答
0

过去我不得不做类似的事情,并在标准库中遇到了一个叫做 linecache 的东西。你可能也想调查一下。

http://docs.python.org/library/linecache.html

于 2012-06-14T16:14:22.623 回答