【问题标题】:How to read file N lines at a time?如何一次读取文件 N 行?
【发布时间】:2011-08-15 12:39:33
【问题描述】:

我需要通过一次最多读取 N 行来读取一个大文件,直到 EOF。在 Python 中最有效的方法是什么?比如:

with open(filename, 'r') as infile:
    while not EOF:
        lines = [get next N lines]
        process(lines)

【问题讨论】:

  • 快速非常愚蠢的问题:如果 N == 1,您将在 process(lines) 中执行的任何操作都有效吗?如果不是,那么您在最后一行中可能存在单行问题。如果它确实适用于 N == 1,那么只做 for line in infile: work_on(line) 会更有效率。
  • @JohnMachin 虽然它可能适用于 N == 1,但它可能效率不高。想想 DL 中的小批量梯度下降。

标签: python file-io iterator


【解决方案1】:

一种解决方案是列表推导和切片运算符:

with open(filename, 'r') as infile:
    lines = [line for line in infile][:N]

lines 之后是行的元组。但是,这会将完整的文件加载到内存中。如果你不想要这个(即如果文件可能真的很大),还有另一个使用生成器表达式和来自 itertools 包的islice 的解决方案:

from itertools import islice
with open(filename, 'r') as infile:
    lines_gen = islice(infile, N)

lines_gen 是一个生成器对象,它为您提供文件的每一行,并且可以在这样的循环中使用:

for line in lines_gen:
    print line

两种解决方案都可以为您提供最多 N 行(或者更少,如果文件没有那么多)。

【讨论】:

  • 简化为lines = islice(infile, N)
  • 注意:它读取 N 行并停止。要阅读接下来的 N 行,您可以将代码包装在一个循环中(直到 EOF)或使用我的答案中所示的 grouper 配方。
  • 这个解决方案没有回答“我如何一次读取 N 行直到 EOF”的问题。它仅提供一次读取 N 行的机制,但仅演示一次读取 N 行(最后的 for 循环)。
  • OP 声明 我需要一次最多读取 N 行来读取一个大文件,而您的第一个解决方案将所有行加载到内存中?!也许您甚至不应该考虑第一个解决方案并将其从您的答案中删除!!!
【解决方案2】:

文件对象是 Python 中行的迭代器。要一次遍历文件 N 行,您可以在文档的 Itertools Recipes 部分中使用 grouper() 函数。 (另见What is the most “pythonic” way to iterate over a list in chunks?):

try:
   from itertools import izip_longest
except ImportError:  # Python 3
    from itertools import zip_longest as izip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return izip_longest(*args, fillvalue=fillvalue)

示例

with open(filename) as f:
     for lines in grouper(f, N, ''):
         assert len(lines) == N
         # process N lines here

【讨论】:

  • @Kevin J. Chase:1- 二进制文件是b'\n'-lines 上的迭代器 2- itertools.izip_longest 在 Python 3 中没有被删除,它被重命名为 itertools.zip_longest
  • 我最想更新该链接,因为代码仅在 Python 2 中按编写方式工作,并且到 docs.python.org 的未指定链接似乎默认为 3 而不是2 现在。 1:确实如此。 2:在 Python 3 中,zip / izip 函数中的哪一个被“删除”是值得商榷的 --- 一个的代码丢失,另一个的名称是。
  • 我不介意编辑。该评论是为了您的利益。 Python 3 中的 itertools.zip_longest() 和 Python 2 中的 itertools.izip_longest() 是同一个对象。
  • @martineau:你为什么要删除 python2 shebang? izip_longest 在 Python 3 中不可用(它在那里重命名为 zip_longest
【解决方案3】:

此代码适用于文件中的任何行数和任何N。如果文件中有1100 linesN = 200,您将获得5 次处理200 行的块和1 次处理100 行的块。

with open(filename, 'r') as infile:
    lines = []
    for line in infile:
        lines.append(line)
        if len(lines) >= N:
            process(lines)
            lines = []
    if len(lines) > 0:
        process(lines)

【讨论】:

    【解决方案4】:

    也许:

    for x in range(N):
      lines.append(f.readline())
    

    【讨论】:

      【解决方案5】:

      我认为您应该使用块而不是指定要读取的行数。它使您的代码更加健壮和通用。即使行很大,使用 chunk 也只会将分配的数据量上传到内存中。

      参考this链接

      【讨论】:

        【解决方案6】:

        我需要一次从文件中读取 n 行以获取超大文件 (~1TB) 并写了一个 simple package 来执行此操作。如果你pip install bigread,你可以这样做:

        from bigread import Reader
        
        stream = Reader(file='large.txt', block_size=10) 
        for i in stream:
          print(i)
        

        block_size 是一次读取的行数。


        此软件包不再维护。我现在觉得最好用:

        with open('big.txt') as f:
          for line_idx, line in enumerate(f):
            print(line)
        

        如果您需要记忆前几行,只需将它们存储在一个列表中。如果您需要知道未来的行来决定如何处理当前行,请将当前行存储在一个列表中,直到您到达该未来行...

        【讨论】:

        • 上面给出的链接似乎坏了,我也无法将它与您在 github 上的任何其他 repos 匹配。 pypi.org/project/bigread 上有一个可用的版本,但它看起来不再维护?
        • 是的,它不再维护:/我更新了上面的答案,以展示我现在如何解决这个问题;我希望这会有所帮助!
        【解决方案7】:

        for 循环怎么样?

        with open(filename, 'r') as infile:
            while not EOF:
                lines = []
                for i in range(next N lines):
                    lines.append(infile.readline())
                process(lines)
        

        【讨论】:

        • 这个语法“下 N 行”是什么伪代码?这里是蟒蛇菜鸟
        • @ColinD 这只是你想要的行数。例如 7 行将是 for i in range(7)
        【解决方案8】:

        你可能需要做一些简单的事情:

        lines = [infile.readline() for _ in range(N)]
        

        更新在 cmets 之后:

        lines = [line for line in [infile.readline() for _ in range(N)] if len(line) ]
        

        【讨论】:

        • 您的代码没有检查行数。例如,如果行数小于 N - 你会得到错误。
        • @Anatolij:你是对的,没有检查 - 但你只是在 EOF 之后得到空字符串并且没有错误。
        • 你需要检查process()中的每一项,所以这是开销。
        【解决方案9】:

        如果您可以提前阅读完整的文件;

        infile = open(filename, 'r').readlines()
        my_block = [line.strip() for line in infile[:N]]
        cur_pos = 0
        while my_block:
            print (my_block)
            cur_pos +=1
            my_block = [line.strip() for line in infile[cur_pos*N:(cur_pos +1)*N]]
        

        【讨论】:

          【解决方案10】:

          我一直在寻找同一个问题的答案,但并不真正喜欢之前提出的任何东西,所以我最终写了这个有点丑陋的东西,它完全符合我的要求没有使用奇怪的库 s>.

          def test(filename, N):
              with open(filename, 'r') as infile:
                  lines = []
                  for line in infile:
                      line = line.strip()
                      if len(lines) < N-1:
                          lines.append(line)
                      else:
                          lines.append(line)
                          res = lines
                          lines = []
                      yield res
                  else:
                      if len(lines) != 0:
                          yield lines
          

          【讨论】:

          • itertools 在 Python 标准库中
          • 还不错,itertools 很好,我对 islice 感觉不太舒服。
          猜你喜欢
          • 2011-09-14
          • 2011-01-30
          • 1970-01-01
          • 2020-02-25
          • 2010-12-18
          • 1970-01-01
          • 2012-10-13
          • 1970-01-01
          相关资源
          最近更新 更多