如何以内存高效的方式在Python中拆分和解析大型文本文件?

1 投票
1 回答
1387 浏览
提问于 2025-04-17 20:10

我有一个比较大的文本文件需要解析。主要的格式大致如下:

step 1

[n1 lines of headers]

  3  3  2
 0.25    0.43   12.62    1.22    8.97
12.89   89.72   34.87   55.45   17.62
 4.25   16.78   98.01    1.16   32.26
 0.90    0.78   11.87
step 2

[n2 != n1 lines of headers]

  3  3  2
 0.25    0.43   12.62    1.22    8.97
12.89   89.72   34.87   55.45   17.62
 4.25   16.78   98.01    1.16   32.26
 0.90    0.78   11.87
step 3

[(n3 != n1) and (n3 !=n2) lines of headers]

  3  3  2
 0.25    0.43   12.62    1.22    8.97
12.89   89.72   34.87   55.45   17.62
 4.25   16.78   98.01    1.16   32.26
 0.90    0.78   11.87

换句话说:

一个分隔符:步骤 #

已知长度的标题(行数,而不是字节数)

数据的三维形状:nz, ny, nx

数据:fortran格式,原始数据集中每行大约有10个浮点数

我只想提取这些数据,把它们转换成浮点数,放到一个numpy数组里,然后根据给定的形状进行调整。

我已经做了一些编程... 主要的思路是:

  1. 首先找到每个分隔符的位置(“步骤 X”)
  2. 跳过 nX(n1, n2...)行 + 1,才能到达数据部分
  3. 从那里开始读取字节,一直到下一个分隔符。

我一开始想避免使用正则表达式,因为这会让速度变得很慢。光是完成第一步(浏览文件以获取每个部分的偏移量)就需要3-4分钟。

问题是我基本上是用 file.tell() 方法来获取分隔符的位置:

[file.tell() - len(sep) for line in file if sep in line]

问题有两个方面:

  1. 对于较小的文件,file.tell() 能给出正确的分隔符位置,但对于较大的文件,它却不行。我怀疑在循环中不应该使用 file.tell(),无论是用显式的 file.readline() 还是隐式的 for line in file(我都试过)。我不知道为什么,但结果就是:对于大文件,[file.tell() for line in file if sep in line]总是给出分隔符后面那行的正确位置。
  2. len(sep) 并不能给出正确的偏移量修正,以返回到“分隔符”行的开头。sep 是一个字符串(字节),包含文件的第一行(第一个分隔符)。

有没有人知道我该怎么解析这个?

附注:我先找到偏移量是因为我想能够在文件中浏览:我可能只想要第10个数据集或者第50000个数据集...

1- 找到偏移量

sep = "step "
with open("myfile") as f_in:
    offsets = [fin.tell() for line in fin if sep in line]

正如我所说,这在简单的例子中是有效的,但在大文件上却不行。

新的测试:

sep = "step "
offsets = []
with open("myfile") as f_in:
    for line in f_in:
        if sep in line:
            print line
            offsets.append(f_in.tell())

打印出的行对应于分隔符,这一点毫无疑问。但用 f_in.tell() 得到的偏移量并不对应下一行。我猜文件是在内存中缓冲的,当我尝试在隐式循环中使用 f_in.tell() 时,我得到的不是当前位置,而是缓冲区的末尾。这只是我的一个大胆猜测。

1 个回答

0

我找到了答案:在文件上使用for循环和tell()函数不太搭。就像把for i in filefile.readline()混在一起会出错一样。

所以,使用file.tell()的时候,最好只配合file.readline()或者file.read()

绝对不要使用

for line in file:
    [do stuff]
    offset = file.tell()

这真的很可惜,但事实就是这样。

撰写回答