获取文本文件的第一行和最后一行的最有效的方法是什么?
我有一个文本文件,其中包含每行的时间戳。 我的目标是find时间范围。 所有的时间都是有序的,所以第一行将是最早的时间,最后一行是最近的时间。 我只需要第一个也是最后一个线。 在python中获取这些行的最有效方法是什么?
注:这些文件的长度相对较大,每个文件大约有一百二十万行,我必须为几百个文件执行此操作。
文档为io模块
with open(fname, 'rb') as fh: first = next(fh).decode() fh.seek(-1024, 2) last = fh.readlines()[-1].decode()
这里的variables值是1024:它表示平均string长度。 我只select1024例如。 如果你有一个平均线长度的估计,你可以使用该值乘以2。
既然你不知道线长度可能的上限,显而易见的解决scheme是循环遍历文件:
for line in fh: pass last = line
你不需要open(fname)
你可以使用open(fname)
的二进制标志。
ETA :因为你有很多文件需要处理,你可以使用random.sample
创build几十个文件的样本,然后运行这个代码来确定最后一行的长度。 随着位置偏移的先验大值(比方说1 MB)。 这将帮助您估算全面运行的价值。
您可以打开文件进行读取,并使用内置的readline()
读取第一行,然后查找文件结尾并向后退出,直到find该行的前一个EOL,并从那里读取最后一行。
with open(file, "rb") as f: first = f.readline() # Read the first line. f.seek(-2, os.SEEK_END) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found... f.seek(-2, os.SEEK_CUR) # ...jump back the read byte plus one more. last = f.readline() # Read last line.
跳到倒数第二个字节而不是最后一个字节可以防止由于尾随EOL而直接返回。 当你退后一步时,你还需要步进两个字节,因为读取和检查EOL将把位置向前推进一步。
当使用seek
,格式是fseek(offset, whence=0)
, whence
表示偏移是相对于什么的。 从docs.python.org引用:
SEEK_SET
或0
=从stream开始寻找(默认); 偏移量必须是由TextIOBase.tell()返回的数字或零。 任何其他偏移值都会产生未定义的行为。SEEK_CUR
或1
=“查找”到当前位置; 偏移必须为零,这是一个无操作(所有其他值不受支持)。SEEK_END
或2
=寻找stream结束; 偏移量必须为零(所有其他值不受支持)。
在timek上运行10k次,总共200kB的6k行的文件给了我1.62s和6.92s的时间,比较早前提到的for循环。 使用一个1.3GB大小的文件,仍然有6k行,一百次导致8.93比86.95。
with open(file, "rb") as f: first = f.readline() # Read the first line. for last in f: pass # Loop through the whole file reading it all.
这是SilentGhost的答案的修改版本,将做你想要的。
with open(fname, 'rb') as fh: first = next(fh) offs = -100 while True: fh.seek(offs, 2) lines = fh.readlines() if len(lines)>1: last = lines[-1] break offs *= 2 print first print last
这里不需要线长度的上限。
你可以使用unix命令吗? 我认为使用head -1
和tail -n 1
可能是最有效的方法。 或者,您可以使用简单的fid.readline()
来获取第一行和fid.readlines()[-1]
,但这可能会占用太多的内存。
首先以读取模式打开文件,然后使用readlines()方法逐行读取。所有行存储在列表中。现在可以使用列表切片获取文件的第一行和最后一行。
a=open('file.txt','rb') lines = a.readlines() if lines: first_line = lines[:1] last_line = lines[-1]
w=open(file.txt, 'r') print ('first line is : ',w.readline()) for line in w: x= line print ('last line is : ',x) w.close()
for
循环遍历行, x
在最后的迭代中获取最后一行。
with open("myfile.txt") as f: lines = f.readlines() first_row = lines[0] print first_row last_row = lines[-1] print last_row
这是我的解决scheme,也与Python3兼容。 它也可以pipe理边界案例,但是却忽略了对utf-16的支持:
def tail(filepath): """ @author Marco Sulla (marcosullaroma@gmail.com) @date May 31, 2016 """ try: filepath.is_file fp = str(filepath) except AttributeError: fp = filepath with open(fp, "rb") as f: size = os.stat(fp).st_size start_pos = 0 if size - 1 < 0 else size - 1 if start_pos != 0: f.seek(start_pos) char = f.read(1) if char == b"\n": start_pos -= 1 f.seek(start_pos) if start_pos == 0: f.seek(start_pos) else: char = "" for pos in range(start_pos, -1, -1): f.seek(pos) char = f.read(1) if char == b"\n": break return f.readline()
这是Trap 的回答和AnotherParker 的评论 。
获得第一条线非常简单。 对于最后一行,假设你知道行长度的近似上限, os.lseek从SEEK_END中find一些量,find第二行到最后一行结束,然后readline()最后一行。
这是@ Trasp的答案的扩展,它具有处理只有一行的文件的angular落情况的附加逻辑。 如果您反复要读取持续更新的文件的最后一行,处理这种情况可能会很有用。 没有这个,如果你试图抓住刚创build并且只有一行的文件的最后一行, IOError: [Errno 22] Invalid argument
将会产生IOError: [Errno 22] Invalid argument
。
def tail(filepath): with open(filepath, "rb") as f: first = f.readline() # Read the first line. f.seek(-2, 2) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found... try: f.seek(-2, 1) # ...jump back the read byte plus one more. except IOError: f.seek(-1, 1) if f.tell() == 0: break last = f.readline() # Read last line. return last