如何开始在特定行迭代文件?

时间:2017-04-11 20:31:54

标签: python file for-loop iteration seek

我正在使用enumerate()迭代文件的行,有时需要在特定文件行开始迭代,所以我尝试testfile.seek(),例如如果我想在第10行再次开始迭代文件,那么testfile.seek(10)

test_file.seek(10)

for i, line in enumerate(test_file):
    …

然而test_file始终在第一行0处开始迭代。 我能做错什么?为什么seek()不起作用?任何更好的实现也将受到赞赏。

提前感谢您,一定会提前/接受回答

5 个答案:

答案 0 :(得分:6)

普通文件是字符序列,在文件系统级别,就Python而言;跳转到特定线路没有低级别的方法。 seek()命令以字节为单位计算偏移量,而不是行数。 (原则上,只有在文件以二进制模式打开时才应使用显式搜索偏移量。查找文本文件为"undefined behavior",因为逻辑字符可能需要多个字节。)

如果您想跳过多行,唯一的选择是阅读并丢弃它们。由于迭代文件对象一次只能获取一行,因此使用itertools.islice()的简单方法是使用https://arxiv.org/pdf/1512.03385.pdf

from itertools import islice

skipped = islice(test_file, 10, None)  # Skip 10 lines, i.e. start at index 10
for i, line in enumerate(skipped, 11):
    print(i, line, end="")
    ...

答案 1 :(得分:1)

我个人只会使用if语句。或许是基本的,但至少很容易理解。

with open("file") as fp:
for i, line in enumerate(fp):
    if i >= 10:
        # do stuff.

编辑:islice: 这里进行的比较:Python fastest access to line in file比我能做的更好。结合itertools手册:https://docs.python.org/2/library/itertools.html我怀疑你需要更多

答案 2 :(得分:1)

本机Python的方法是使用zip迭代不必要的行。

with open("text.txt","r") as test_file:
    for _ in zip(range(10), test_file): pass
    for i, line in enumerate(test_file,start=10):
        print(i, line)

答案 3 :(得分:0)

seek方法将帮助您的唯一方法是,如果文件中的所有行都具有相同的长度,您提前知道这些行,并且您的文件是二进制或至少ascii-only文本(即不允许使用unicode字符)。那你真的可以做到

test_file.seek(10 * (length_of_line + 1), os.SEEK_SET)

这是因为seek将内部文件指针移动固定的字节数,而不是行。上面的+1用于说明换行符。您可能需要在Windows机器上进行+2

如果你的文件是非ascii,这将不起作用,因为某些行的字符长度可能相同但实际上包含不同的字节数,因此调用seek会产生未定义的结果。

有几种合法的方法可以跳过前10行:

  1. 将整个文件读入list并丢弃前10行:

    with open(...) as test_file:
        test_data = list(test_file)[10:]
    

    现在test_data除了前10个文件外,还包含文件中的所有行。

  2. 使用forenumerate循环中读取文件时,放弃文件中的行:

    with open(...) as test_file:
        for lineno, line in test_file:
            if lineno < 10:
                continue
            # Do something with the line
    

    这种方法的优点是可以在将每条线移到您之前从每条线上剥离尾随换行符。这在功能上类似于使用itertools.islice,因为其他一些答案表明了这一点。

  3. 在正常进行之前,使用一些非常神秘的低级内容实际读取文件中的10个换行符。您可能必须预先指定文件的编码才能使其与文本I / O一起正常工作,但它对于ASCII文件应该是开箱即用的(有关详细信息,请参阅here):

    newline_count = 10
    with open(..., encoding='utf-8') as test_file:
        while newline_count > 0:
            next_char = test_file.read(1)
            if next_char == '\n':
                newline_count -= 1
        # You have skipped 10 lines, so process normally here.
    

    此选项不是特别健壮。它不能处理优雅地少于10行的情况,并且它非常粗略地重新实现内置文件迭代器的内部机制。它提供的唯一可能的优点是它不像迭代器那样缓冲整行。

答案 4 :(得分:0)

除非您知道所需行的第一个字符的字节偏移量,否则不能使用seek()到达特定行的开头。

一种简单的方法是在itertools模块中使用islice()迭代器。

例如,假设您有一个非常类似的测试输入文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...

示例代码:

from __future__ import print_function
from itertools import islice

with open('test_file.txt') as test_file:
    for i, line in enumerate(islice(test_file, 9, None), 10):
        print('line #{}: {}'.format(i, line), end='')

输出:

line #10: 10
line #11: 11
line #12: 12
line #13: 13
line #14: 14
line #15: 15
line #16: 16
line #17: 17
line #18: 18
line #19: 19
line #20: 20
line #21: 21
line #22: 22
...

注意islice()从零开始计数,这就是为什么它的第一个参数是9而不是10。这也不像seek()那么快,因为islice()实际上读取了所有行,直到它到达你想要开始的那一行。