从文件中读取n行数据(但不是全部)的Python代码

3
如何在迭代文件时读取n行而不是只读取一行?我有一个结构良好的文件,我想做类似于这样的事情:
for line1, line2, line3 in file:
    do_something(line1)
    do_something_different(line2)
    do_something_else(line3)

但是它无法正常工作:

值错误:拆包的值太多

目前我正在这样做:

for line in file:
    do_someting(line)
    newline = file.readline()
    do_something_else(newline)
    newline = file.readline()
    do_something_different(newline)
... etc.

这很糟糕,因为我正在写无尽的 'newline = file.readline()',这些代码使得代码混乱不堪。 有没有聪明的方法可以解决这个问题?(我真的想避免一次性读取整个文件,因为它非常大)


file 是 Python 中的可迭代对象,请参考 https://dev59.com/VHRC5IYBdhLWcg3wCMc6#434411 - undefined
11个回答

6
基本上,你的file是一个迭代器,它每次只返回文件的一行。这将使你的问题变成如何从迭代器中一次性返回多个元素。在这个问题中提供了一个解决方案。请注意,islice函数在itertools模块中,所以你需要从那里导入它。

3
如果是xml,为什么不直接使用lxml呢?

因为我只是对字符串进行非常简单的操作,不需要解析器来读取所有的标签等;我只想对第一行使用"for a",第二行使用"for b",...,第十行使用"for j",然后循环进行第11、12、13...20行等操作。 - undefined
3
这是语义数据还是不是?请不要将xml数据视为文本。这会让pandas变得很慢。而且lxml非常快。 - undefined

2

You could use a helper function like this:

def readnlines(f, n):
    lines = []
    for x in range(0, n):
        lines.append(f.readline())
    return lines

然后你可以按照自己的意愿进行操作:
while True:
    line1, line2, line3 = readnlines(file, 3)
    do_stuff(line1)
    do_stuff(line2)
    do_stuff(line3)

话虽如此,如果您正在使用xml文件,长期来看,使用真正的xml解析器可能会让您更加满意...


好主意,但你不能像那样迭代一个函数。你需要使用yield关键字来创建一个生成器。另外,这也是使用列表推导式的好地方。 - undefined
还不错,但是你的while循环永远不会停止。我在下面发布了一个使用生成器的版本。 - undefined

2

itertools来帮助解决问题:

import itertools
def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)


fobj= open(yourfile, "r")
for line1, line2, line3 in grouper(3, fobj):
    pass

1

for i in file 会产生一个 str,所以你不能像这样批量读取三个内容: for i, j, k in file(尝试执行 a, b, c = 'bar'a, b, c = 'too many characters',查看 a、b、c 的值来了解为什么会出现“too many values to unpack”)。

如果你要对每行执行相同的操作,并且只是想在某个时候停止,请按照以下方式实现:

for line in file_handle:
    do_something(line)
    if some_condition:
        break  # Don't want to read anything else

(另外,不要将file用作变量名,这会遮蔽内置函数。)

0
如果你想能够反复使用这些数据,一种方法可能是这样做:
lines = []
for line in file_handle:
    lines.append(line)

这将给你一个列表的行,你可以通过索引访问它们。另外,当你说一个巨大的文件时,很可能无关紧要文件有多大,因为Python可以非常快速地处理成千上万行的内容。

我不想一遍又一遍地使用这些数据;我希望读取10行数据,对它们进行10种不同的操作,然后再读取另外10行数据并重复此过程。 - undefined
在我看来,这仍然是首选的方法。如果你在一个方法中进行数据操作,并且将列表放在其中,它将被垃圾收集器清除,因此不会使用过多的内存,并且你可以按任意顺序访问任何索引。 - undefined
这种方法是不可能的,因为我不想编写一段在文件超过内存大小时会崩溃的代码。如果我们每次都可以将文件加载到列表中,那么就不需要文件数据类型或readline()方法了,只需要一个readall_and_put_into_list(file)方法就可以了。这不是正确的做事方式。 - undefined

0
为什么你不能这样做呢:
ctr = 0
对于每一行在文件中:
  if ctr == 0:

     ....

  elif ctr == 1:

     ....

  ctr = ctr + 1

如果你觉得if/elif结构很丑陋,你可以创建一个哈希表或函数指针列表,然后这样做:
对于文件中的每一行:
   function_list[ctr]()

或类似的内容


0

可以通过巧妙地使用zip函数来实现。这个方法很简短,但对我来说有点神秘(很难看出它是如何工作的)。它会截断任何不填满一组的末尾行,这可能是好事或坏事,取决于你要做什么。如果你需要最后几行,itertools.izip_longest可能能解决问题。

zip(*[iter(inputfile)] * 3)

更明确和灵活地进行操作,这是对Mats Ekberg解决方案的修改:
def groupsoflines(f, n):
    while True:
        group = []
        for i in range(n):
            try:
                group.append(next(f))
            except StopIteration:
                if group:
                    tofill = n - len(group)
                    yield group + [None] * tofill
                return
        yield group

for line1, line2, line3 in groupsoflines(inputfile, 3):
    ...

注意:如果在一组的中途用完了行数,它会用None填充空缺,这样你仍然可以解包。因此,如果你的文件行数可能不是三的倍数,你需要检查line2line3是否为None


0

听起来你正在尝试并行读取磁盘...这确实很难做到。给你的所有解决方案都是切实可行的。不要仅仅因为代码“看起来丑陋”而放弃。最重要的是它有多高效/有效,如果代码凌乱,你可以整理一下,但不要因为不喜欢某种代码方式的外观而寻找全新的方法来做同样的事情。

至于内存耗尽问题,你可能想查看pickle


0
如果你在做同样的事情,为什么需要在每次迭代中处理多行?
对于文件中的每一行,使用“for line in file”是一个好方法。它通常比手动读取文件更高效,无论是在io性能还是内存方面。

抱歉,我编辑了一下。我想对每个批次中的n行执行不同的操作,然后对另一个批次中的n个文件的行执行相同的操作。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接