【问题标题】:Cancel last line iteration on a file取消文件的最后一行迭代
【发布时间】:2015-08-30 15:38:16
【问题描述】:

我需要迭代一个文件,在一个条件上停止迭代,然后在同一行使用另一个函数继续解析文件(这可能会改变,所以我不能只在前一个函数中添加内容)。

一个示例文件(file.txt):

1
2
3
4
5
6
7
8
9

我尝试做的功能:

def parse1(file, stop):
# 1st parsing function (Main function I am doing)
    for line in file:
            if line.strip() == stop:
            # Stop parsing on condition
                break
            else:
            # Parse the line (just print for example)
                print(line)

def parse2(file):
# 2nd parsing function (Will be my own functions or external functions)
    for line in file:
        # Parse the line (just print for example)
        print(line)

终端结果:

>>> file = open("file.txt")

>>> parse1(file, "4")
1
2
3

>>> parse2(file)
5
6
7
8
9

我的问题是当我查找条件时,第一个函数跳过了“4”行。

我怎样才能避免这种情况:我找到了取消最后一次迭代或返回一行的任何解决方案。

file.tell() 函数不适用于文件中的for

我尝试使用 while + file.readline() 执行此操作,但它比文件上的 for 循环慢得多(而且我想解析数百万行的文件)。

是否有一个优雅的解决方案来保持for 循环的使用?

【问题讨论】:

  • 你不能保留 parse1 中的 line 变量并将其传递给 parse2
  • 这个想法对我自己的函数很好,但我可能会使用一些外部函数来代替parse2,这些函数没有这样的参数。

标签: python python-3.x for-loop file-io


【解决方案1】:

在 python3 中,'for line in file' 结构在内部由迭代器表示。根据定义,从迭代器生成的值不能“放回”以供以后使用 (http://www.diveintopython3.net/iterators.html)。

要获得所需的行为,您需要一个将两个迭代器链接在一起的函数,例如itertools 模块提供的chain 函数。在parse1的停止条件下,将最后一行连同文件迭代器一起返回:

import itertools

def parse1(file,stop):
# 1st parsing function
    for line in file:
       # Stop parsing on condition
        if line.strip() == stop:
            return itertools.chain([line],file) # important line
        else:
        # Parse the line (just print for example)
            print('parse1: '+line)

chain 语句连接两个迭代器。第一个迭代器只包含一个元素:您要再次处理的行。第二个迭代器是文件的剩余部分。一旦第一个迭代器的值用完,就会访问第二个迭代器。

您无需更改parse2。为了清楚起见,我修改了打印语句:

def parse2(file):
# 2nd parsing function
for line in file:
    # Parse the line (just print for example)
    print('parse2: '+line)

然后,您可以以最实用的方式调用 parse1 和 parse2:

with open('testfile','r') as infile:
   parse2(parse1(infile,'4'))

上面一行的输出是:

parse1: 1
parse1: 2
parse1: 3
parse2: 4
parse2: 5
parse2: 6
parse2: 7
parse2: 8
parse2: 9

注意,值“4”是如何由 parse2 函数产生的。

【讨论】:

  • 谢谢,这正是我需要的!
【解决方案2】:

我建议制作文件对象的副本1 并在else 块中迭代副本并在第一个函数中调用第二个函数,这也是您可以使用的更pythonic 方式with 用于打开文件的语句将在语句末尾关闭文件并将第二个函数放在第一个函数中:

#ex.txt

1
2
3
4
5
6
7
8
9
10

您可以使用itertools.tee 创建文件对象的副本1

from itertools import tee

def parse1(file_name, stop):

  def parse2(file_obj):
    print '**********'
    for line in file_obj:
        print(line)

  with open(file_name) as file_obj:
    temp,file_obj=tee(file_obj)
    for line in temp:
            if line.strip() == stop:
                break
            else:
                next(file_obj)
                print(line)
    parse2(file_obj)

parse1("ex.txt",'4')

结果:

1

2

3

**********
4

5

6

7

8

9

10

1) 实际上itertools.tee 不会创建副本,但您可以根据 DOC 将其用于此目的 从单个可迭代对象中返回 n 个独立的迭代器。 并且您可以将其中一个独立的迭代器分配给已迭代的对象本身并创建另一个作为 temp。

【讨论】:

  • @Anc 欢迎,我认为将您的功能合二为一更高效、更安全!
  • 我同意只创建一个函数更有效,但对于我的情况,我需要灵活性(我想要解析的文件类型可能会有所不同)。
【解决方案3】:

恕我直言,最简单的解决方案是让第一个解析器返回找到停止条件的行,并将其传递给第二个解析器。第二个应该有一个明确的函数来解析一行以避免代码重复:

def parse1(file, stop):
# 1st parsing function (Main function I am doing)
    for line in file:
            if line.strip() == stop:
            # Stop parsing on condition
                return line
            else:
            # Parse the line (just print for example)
                print(line)
    return None

def parse2(file, line = None):
# 2nd parsing function (Will be my own functions or external functions)
    def doParse(line):
    # do actual parsing (just print for example)
        print(line)
    if line is None:
        doParse(line)
    for line in file:
        doParse(line)

# main
...
stop = parse1(file)
if stop:
    parse2(stop, file)

【讨论】:

  • 这个想法适用于我自己的函数,但如果我想使用一些外部函数来代替没有像这样的参数的 parse2,那就行不通了。
猜你喜欢
  • 2018-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-22
  • 1970-01-01
  • 2012-07-22
  • 1970-01-01
  • 2011-10-15
相关资源
最近更新 更多