我必须解析一个巨大的(250 MB)文本文件,由于某种原因只有一行,导致我尝试的每个文本编辑器(Notepad ++,Visual Studio,Matlab)都无法加载它。因此,我逐个阅读它,并在完全读取逻辑行(以#
开头)时解析它:
f = open(filename, "rt")
line = ""
buffer = "blub"
while buffer != "":
buffer = f.read(10000)
i = buffer.find('#')
if i != -1: # end of line found
line += buffer[:i]
ProcessLine(line)
line = buffer[i+1:] # skip the '#'
else: # still reading current line
line += buffer
这种方法运行得相当不错,但是,它可能会发生,一条线比我的缓冲区短,这会导致我跳过一条线。所以我用
替换了循环while buffer != "":
buffer = f.read(10000)
i = buffer.find('#')
while i != -1:
pixels += 1
line += buffer[:i]
buffer = buffer[i+1:]
ProcessLine(line)
i = buffer.find('#')
line += buffer
,这就是诀窍。然而,这至少要慢一百倍,因此无法读取大文件。我没有真正看到,这是怎么发生的,我确实有一个内循环,但大多数时候它只重复一次。此外,我可能会复制缓冲区(buffer = buffer[i+1:]
),我可以从中了解性能是否会下降一半,但我不知道这会让它变得慢100倍。
作为旁注:我的(逻辑)行大约是27.000字节。因此,如果我的缓冲区是10.000字节,我从不跳过第一个实现中的行,如果它是30.000,我这样做。然而,这似乎不会影响性能,即使第二次实现中的内部循环最多被评估一次,性能仍然很糟糕。
发生了什么事,我想念?
答案 0 :(得分:2)
如果我理解你想要做什么,那么两个版本的代码都是错误的。就像@Leon在第二个版本中所说的那样,line = ""
之后缺少ProcessLine(line)
,而在第一个版本中只是第一行是正确的,而且如果你想让行比缓冲更短,那么你只使用第一部分line += buffer[:i]
中的缓冲区,但问题在于line = buffer[i+1:]
,所以如果line
长度为1000个字符,buffer
长度为10000个字符,那么当您使用{时{1}},您的行长度为9000个字符,可能包含多行。阅读:
“这样做效果相当不错,但可能会发生,一条线比我的缓冲区短,这会导致我跳过一条线”
我认为你意识到了这一点,但我正在详细撰写的原因是,这也是你的第一个版本更快运行的原因。
在解释之后,我认为最好的方法是读取孔文件然后拆分文本以获取行,所以你的代码看起来像这样:
line += buffer[:i]
并且你可以使用类似的东西:
f = open('textfile.txt', "rt")
buffer = f.read()
f.close()
l = buffer.split('#')
获取列表for line in l:
ProcessLine(line)
我花了不到2秒钟。
PS:使用记事本打开大文件(如250MB)不会有问题,我甚至打开了500MB文件。
答案 1 :(得分:1)
您的第二个版本不仅工作速度慢,而且工作也不正确。
在您的第一个版本中,您使用分配(line
)重置line = buffer[i+1:]
,而在第二个版本中,您只会附加到line
。因此,在第二个版本中,最后line
包含文件的全部内容,而不是#
符号。
通过在处理后立即清除line
来修复代码:
while buffer != "":
buffer = f.read(10000)
i = buffer.find('#')
while i != -1:
pixels += 1
line += buffer[:i]
buffer = buffer[i+1:]
ProcessLine(line)
line = "" # sic!
i = buffer.find('#')
line += buffer