以下代码是否有其他选择:
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)
,并且需要跳转到我事先知道的特定行?当我知道我至少可以忽略文件的前半部分时,我会逐个处理它们。寻找更优雅的解决方案,如果有的话。
答案 0 :(得分:103)
如果没有在文件中读取至少一次,就无法前进,因为你不知道换行的位置。你可以这样做:
# 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])
答案 1 :(得分:27)
答案 2 :(得分:20)
如果线条的长度不同,你真的没有那么多选项...你可能需要处理行结束字符以了解你何时进展到下一行。
然而,您可以通过将最后一个参数更改为“打开”到不是0的内容来显着提高速度并减少内存使用量。
0表示文件读取操作是无缓冲的,这非常慢并且磁盘密集。 1表示文件是行缓冲的,这将是一种改进。大于1的任何东西(比如8k ......即:8096或更高)将文件的块读取到内存中。您仍然可以通过for line in open(etc):
访问它,但是python一次只能进行一些操作,在处理后丢弃每个缓冲的块。
答案 3 :(得分:13)
我可能被丰富的公羊宠坏了,但15米并不大。使用readlines()
读入内存是我通常使用这种大小的文件。之后访问一行是微不足道的。
答案 4 :(得分:5)
由于无法在不读取所有行的情况下确定所有行的长度,因此您别无选择,只能在起始行之前遍历所有行。你所能做的就是让它看起来不错。如果文件非常庞大,那么您可能希望使用基于生成器的方法:
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)
注意:基于此方法,索引为零。
答案 5 :(得分:4)
我很惊讶没人提到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
答案 6 :(得分:3)
如果您事先知道文件中的位置(而不是行号),则可以使用file.seek()转到该位置。
编辑:您可以使用linecache.getline(filename, lineno)函数,该函数将返回行lineno的内容,但仅在将整个文件读入内存后才会返回。好的,如果你从文件中随机访问行(就像python本身可能想要打印回溯一样)但对15MB文件不好。
答案 7 :(得分:3)
如果您不想在内存中读取整个文件..您可能需要提供除纯文本以外的某种格式。
当然这一切都取决于你想要做什么,以及你多久跳过一次文件。
例如,如果您要在同一个文件中多次跳转行,并且您知道该文件在使用时没有更改,则可以执行以下操作:
首先,遍历整个文件,并记录一些关键行号的“搜索位置”(例如,1000行),
然后,如果您想要12005行,请跳转到12000(您已录制)的位置,然后读取5行,您将知道您在12005行
等等
答案 8 :(得分:3)
生成您要处理的文件的原因是什么?如果它是您可以控制的,您可以在附加文件时生成索引(该行位于哪个位置。)。索引文件可以是固定的行大小(空格填充或0填充数字),肯定会更小。因此可以快速阅读和处理。
答案 9 :(得分:3)
我遇到了同样的问题(需要从特定文件的特定行检索)。
当然,我可以每次遍历文件中的所有记录并在计数器等于目标行时停止它,但是当您想要获得多个特定行时,它不能有效地工作。这导致了主要问题的解决 - 如何直接处理文件的必要位置。
我发现了下一个决定: 首先,我用每行的起始位置完成了字典(键是行号,以及前一行的值 - 累积长度)。
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
最终,目标函数:
def give_line(line_number):
t.seek(dict_pos.get(line_number))
line = t.readline()
return line
t.seek(line_number) - 执行文件修剪直到行初始化的命令。 因此,如果您下次提交readline - 您将获得目标行。
使用这种方法我节省了大部分时间。
答案 10 :(得分:2)
这些行本身是否包含任何索引信息?如果每行的内容类似于“<line index>:Data
”,则可以使用seek()
方法对文件进行二进制搜索,即使Data
的数量是可变的。你要寻找文件的中点,读一行,检查它的索引是否高于或低于你想要的那个,等等。
否则,您所能做的最好就是readlines()
。如果您不想阅读所有15MB,则可以使用sizehint
参数至少用readline()
的较少次调用替换大量readlines()
。
答案 11 :(得分:1)
这是一个使用'readlines(sizehint)'一次读取一行行的示例。 DNS指出了解决方案。我写这个例子是因为这里的其他例子是单行导向的。
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)
答案 12 :(得分:1)
您可以使用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(偏移)移动到您需要的行
答案 13 :(得分:1)
如果您正在处理文本文件&amp;基于 linux system ,您可以使用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)
答案 14 :(得分:0)
没有一个答案特别令人满意,因此这里有一个小片段可以帮助您。
class LineSeekableFile:
def __init__(self, seekable):
self.fin = seekable
self.line_map = list() # Map from line index -> file position.
self.line_map.append(0)
while seekable.readline():
self.line_map.append(seekable.tell())
def __getitem__(self, index):
# NOTE: This assumes that you're not reading the file sequentially.
# For that, just use 'for line in file'.
self.fin.seek(self.line_map[index])
return self.fin.readline()
用法示例:
In: !cat /tmp/test.txt
Out:
Line zero.
Line one!
Line three.
End of file, line four.
In:
with open("/tmp/test.txt", 'rt') as fin:
seeker = LineSeekableFile(fin)
print(seeker[1])
Out:
Line one!
这涉及到很多文件查找,但是对于无法将整个文件容纳在内存中的情况很有用。它会进行一次初始读取以获取行位置(因此它会读取整个文件,但不会将其全部保留在内存中),然后每次访问都会根据事实查找文件。
我根据用户的判断,根据MIT或Apache许可提供了以上代码段。