如何跳转到一个巨大的文本文件中的特定行?

下面的代码是否有其他select:

startFromLine = 141978 # or whatever line I need to jump to urlsfile = open(filename, "rb", 0) linesCounter = 1 for line in urlsfile: if linesCounter > startFromLine: DoSomethingWithThisLine(line) linesCounter += 1 

如果我正在处理一个巨大的文本文件(~15MB)与未知但长度不同的行,并需要跳转到我知道的事先知道的特定行? 当我知道我至less可以忽略文件的前半部分时,我一个接一个地处理它们,我感觉不好。 如果有的话,寻找更优雅的解决scheme。

linecache :

linecache模块允许从Python源文件中获取任何行,同时尝试使用caching进行内部优化,这是一种常见的情况,即从单个文件中读取许多行。 这被traceback模块用来检索包含在格式化回溯中的源代码行。

因为不知道换行符在哪里,所以不能在文件中至less读取一次。 你可以做这样的事情:

 # Read in the file once and build a list of line offsets line_offset = [] offset = 0 for line in file: line_offset.append(offset) offset += len(line) file.seek(0) # Now, to skip to line n (with the first line being line 0), just do file.seek(line_offset[n]) 

如果线条的长度不同,则不会有这么多的选项…您可能需要处理结束字符的行,以了解何时您已经进入下一行。

但是,您可以显着加快速度,并通过将最后一个参数更改为“未打开”来减less内存使用量。

0表示文件读取操作是无缓冲的,这是非常缓慢的和磁盘密集型的。 1表示文件是行缓冲的,这是一个改进。 高于1的任何东西(比如8k,即:8096或更高)将文件的块读入内存。 你仍然可以通过for line in open(etc):访问它,但是python只会一次一点点地处理掉每个缓冲块。

我可能被大量的公羊宠坏了,但是15米并不是很大。 用readlines()读入内存是我通常用这种大小的文件做的。 之后访问一条线是微不足道的。

由于没有办法确定所有行的长度而没有读取它们,所以您别无select,只能遍历起始行之前的所有行。 你所能做的就是让它看起来不错。 如果文件真的很大,那么你可能需要使用基于生成器的方法:

 from itertools import dropwhile def iterate_from_line(f, start_from_line): return (l for i, l in dropwhile(lambda x: x[0] < start_from_line, enumerate(f))) for line in iterate_from_line(open(filename, "r", 0), 141978): DoSomethingWithThisLine(line) 

注意:这个方法的索引是零。

如果事先知道文件中的位置(而不是行号),则可以使用file.seek()转到该位置。

编辑 :你可以使用linecache.getline(filename,lineno)函数,它将返回lineno的内容,但是只有在将整个文件读入内存之后。 很好,如果你随机访问文件内的行(python本身可能想要打印回溯),但对15MB文件不好。

如果你不想读取内存中的整个文件..你可能需要想出纯文本以外的格式。

当然,这一切都取决于你想要做什么,以及你跳过文件的频率。

例如,如果您要在同一个文件中跳转多次 ,并且您知道该文件在处理时不会改变,那么可以这样做:
首先,通过整个文件,并logging一些关键线号(例如1000行)的“查找位置”
然后,如果你想要12005行,跳到12000(你logging的)的位置,然后读5行,你会知道你在12005行,等等

什么生成你想要处理的文件? 如果它在你的控制之下,那么你可以在文件附加到的时候生成一个索引(哪一行在哪个位置)。 索引文件可以是固定行大小(空格填充或0填充数字),一定会更小。 因此可以被轻易地读取和处理。

  • 你想要哪一行?
  • 计算索引文件中相应行号的字节偏移量(因为索引文件的行大小是恒定的)。
  • 使用seek或其他来直接跳转到从索引文件中获取行。
  • parsing得到实际文件对应行的字节偏移量。

我很惊讶没有人提到islice

 line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line 

或者如果你想要整个文件的其余部分

 rest_of_file = itertools.islice(Fhandle,index_of_interest) for line in rest_of_file: print line 

或者如果你想从文件中的每一行

 rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2) for odd_line in rest_of_file: print odd_line 

这些行本身是否包含任何索引信息? 如果每一行的内容都是“ <line index>:Data ”,那么即使Data量是可变的, seek()方法也可以用来对文件进行二进制search。 你会寻找文件的中点,读一行,检查它的索引是高于还是低于你想要的等等。

否则,你可以做的最好的只是readlines() 。 如果你不想读取所有的15MB,你可以使用sizehint参数来至less用sizehintreadline()调用readlines()来代替readlines()

我有同样的问题(需要从巨大的文件特定线检索)。

当然,我可以每次遍历文件中的所有logging,当计数器等于目标行时停止它,但是当你想获得复数个特定行的情况下,它不起作用。 那引起了主要问题的解决 – 如何直接处理文件的必要位置。

我发现下一个决定:首先,我完成了每行的起始位置的字典(关键是行号,以及先前行的累计值)。

 t = open(file,'r') dict_pos = {} kolvo = 0 length = 0 for each in t: dict_pos[kolvo] = length length = length+len(each) kolvo = kolvo+1 

最终瞄准function:

 def give_line(line_number): t.seek(dict_pos.get(line_number)) line = t.readline() return line 

t.seek(line_number) – 执行文件修剪的命令。 所以,如果你下一个提交readline – 你得到你的目标线。

使用这种方法,我节省了大量的时间。

这是一个使用“readlines(sizehint)”来读取一行代码的例子。 DNS指出了这个解决scheme。 我写了这个例子,因为这里的其他例子是单行导向的。

 def getlineno(filename, lineno): if lineno < 1: raise TypeError("First line is line 1") f = open(filename) lines_read = 0 while 1: lines = f.readlines(100000) if not lines: return None if lines_read + len(lines) >= lineno: return lines[lineno-lines_read-1] lines_read += len(lines) print getlineno("nci_09425001_09450000.smi", 12000) 

您可以使用mmap来查找行的偏移量。 MMap似乎是处理文件的最快方法

例:

 with open('input_file', "r+b") as f: mapped = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) i = 1 for line in iter(mapped.readline, ""): if i == Line_I_want_to_jump: offsets = mapped.tell() i+=1 

然后使用f.seek(偏移)移动到你需要的行

如果你正在处理一个基于linux系统文本文件 ,你可以使用linux命令。
对我来说,这工作得很好!

 import commands def read_line(path, line=1): return commands.getoutput('head -%s %s | tail -1' % (line, path)) line_to_jump = 141978 read_line("path_to_large_text_file", line_to_jump) 

可以用这个函数返回第n行:

 def skipton(infile, n): with open(infile,'r') as fi: for i in range(n-1): fi.next() return fi.next()