【问题标题】:python: zip file iterators together that extract lines by nextpython:压缩文件迭代器一起提取行
【发布时间】:2017-04-05 11:21:28
【问题描述】:

拥有多行数据文件,例如喜欢f1:

name1
34
name2
12
name3
331

f2:

name1
0.34
name2
0.1
name3
1.0

为了解析单个文件,我使用如下成语:

with open(f1, "r") as f1: 
    while True:      
        name, data = next(f1), next(f1)
        # Do something with name and data...

如何使用相同的模式来读取“配对”文件,例如 f1f2

def multi_parser(f1, f2):
    with open(f1, "r") as f1_in, open(f2, "r") as f2_in:    
        while True:
            name, data = list(zip(next(f1_in).rstrip(), next(f2_in).rstrip())), 
                         list(zip(next(f1_in).rstrip(), next(f2_in).rstrip()))

            # Do something with name and data...
            print(list(name))
            print(list(data))

似乎代码很贪心,namedata 的第一个打印给出:

[('n', 'n'), ('a', 'a'), ('m', 'm'), ('e', 'e'), ('1', '1')]
[('3', '0'), ('4', '.')]

我期望的地方是:

[('name1', 'name1')]
[('34', '0.34')]

【问题讨论】:

  • 除此之外的裸体是怎么回事?
  • 可能是别的东西 - 它只是没有给出任何警告或任何东西。不过,我不应该与我的意外结果有任何关系
  • zip 接受迭代并返回项目列。在这种情况下,您正在传递字符串,这就是您获得字符的原因。您只需将文件对象传递给zipzip(f1_in, f2_in) 或者如果你想去除线条:zip(map(str.rstrip, f1_in), map(str.rstrip, f2_in))
  • 我需要为每条记录做一些事情(即每两行)
  • @TadhgMcDonald-Jensen 你可以使用zip,然后在每次迭代后使用next,或者使用另一个zipitertools.islicezip(islice(pre_ziped, 0, None, 2),islice(pre_ziped, 1, None, 2))

标签: python list io iterator


【解决方案1】:

问题是next 返回字符串并且你迭代它们(这给你单个字符对)。您可以只对 listtuple 使用文字:

name, data = [(next(f1_in), next(f2_in))], [(next(f1_in), next(f2_in))]

或者,如果您想避免所有这些 next 调用:

f1_in, f2_in = iter(f1_in), iter(f2_in)  # make sure f1_in and f2_in are iterators

for name1, name2, value1, value2 in zip(f1_in, f2_in, f1_in, f2_in):
    name = [(name1, name2)]
    data = [(value1, value2)]

【讨论】:

  • 为每一行调用next 效率不高。您可以简单地zip 文件对象。
  • @Kasramvd:你能详细说明一下吗?
  • @user3375672 阅读我对您问题的评论。关于next,它仅在您想从可迭代而不是所有疯狂的行中获取第一个或一些主要项目时有用:)。
  • 可能值得为更通用的情况添加检查,例如if not isinstance(f1,collections.Iterator): f1 = iter(f1)
  • 是的,因为 nextzip 中被隐式调用(作为 C 代码)而不是显式调用(作为 Python 代码)。至少在 CPython 中。
【解决方案2】:

我建议拆分代码,首先创建一个生成器函数,一次生成两个元素:

def pairs(file):
    try:
        while True: #broken by StopIteration
            yield next(file), next(file)
    except StopIteration:
        return

这种遍历单个文件的方式可以使用 for 循环:

for name, data in pairs(f1):
    print(name, data)

然后要同时遍历两个文件,您可以使用zip 轻松完成:

for (name1, data1),(name2,data2) in zip(pairs(f1),pairs(f2)):
    print(name1, data1)
    print(name2,data2)

我突然想到我对pairs 的定义是这样的:

return zip(file, file)

所以从技术上讲,您可以按照以下方式进行迭代:

for (name1, data1), (name2, data2) in zip(zip(f1,f1),zip(f2,f2)):
    print(name1, data1)
    print(name2, data2)

但我不建议在没有定义 pairs 函数(即使它只是一个返回)并对其进行良好评论的情况下这样做。

【讨论】:

  • 现在更好 (+) 但正如 MSeifert 建议的那样,您可以将文件对象传递给一个 zip
  • 我不喜欢这个成语,它是唯一适用于迭代器但不适用于可迭代对象的东西之一。 (但不会引发有用的错误)
【解决方案3】:
def my_iter(f1, f2):
    while True:
        yield [next(f1).strip(), next(f2).strip()], [next(f1).strip(), next(f2).strip()]


with open('f1', 'r') as f1, open('f2', 'r') as f2:
    for name, data in my_iter(f1, f2):
        print(name)
        print(data)

你可以得到如下结果:

['name1', 'name1']
['34', '0.34']
['name2', 'name2']
['12', '0.1']
['name3', 'name3']
['331', '1.0']

【讨论】:

    【解决方案4】:

    问题出现了,因为您将两个字符串传递给zip,即文件的相应行。 zip 然后在这些字符串的字符对上返回一个迭代器:

    list(zip("asdf", "ghjk"))
    

    给予

    [('a', 'g'), ('s', 'h'), ('d', 'j'), ('f', 'k')]
    

    如果你想要一个完整行的元组,你可以这样做:

    name, data = ([(next(f1_in).rstrip(), next(f2_in).rstrip())], 
                  [(next(f1_in).rstrip(), next(f2_in).rstrip())])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-15
      • 2015-08-28
      • 1970-01-01
      • 2020-01-05
      • 1970-01-01
      相关资源
      最近更新 更多