Почему takewhile () пропускает первую строку? - PullRequest
2 голосов
/ 02 сентября 2011

У меня есть такой файл:

1
2
3
TAB
1
2
3
TAB

Я хочу прочитать строки между TAB как блоки.

import itertools

def block_generator(file):
    with open(file) as lines:
        for line in lines:
            block = list(itertools.takewhile(lambda x: x.rstrip('\n') != '\t',
                                             lines))
            yield block

Я хочу использовать его как таковой:

blocks = block_generator(myfile)
for block in blocks:
    do_something(block)

Блоки, которые я получаю, начинаются со второй строки, как [2,3] [2,3], почему?

Ответы [ 4 ]

4 голосов
/ 02 сентября 2011

Вот еще один подход с использованием groupby

from itertools import groupby
def block_generator(filename):
    with open(filename) as lines:
        for pred,block in groupby(lines, "\t\n".__ne__):
            if pred:
                yield block
2 голосов
/ 02 сентября 2011

Вот, пожалуйста, проверенный код. Использует while True: для цикла и позволяет itertools.takewhile() делать все с lines. Когда itertools.takewhile() достигает конца ввода, он возвращает итератор, который ничего не делает, кроме повышения StopIteration, которое list() просто превращается в пустой список, поэтому простой тест if not block: обнаруживает пустой список и вырывается из цикл.

import itertools

def not_tabline(line):
    return '\t' != line.rstrip('\n')

def block_generator(file):
    with open(file) as lines:
        while True:
            block = list(itertools.takewhile(not_tabline, lines))
            if not block:
                break
            yield block

for block in block_generator("test.txt"):
    print "BLOCK:"
    print block

Как отмечено в комментарии ниже, у этого есть один недостаток: если входной текст имеет две строки подряд только с символом табуляции, этот цикл прекратит обработку без чтения всего входного текста. И я не могу придумать способ справиться с этим чисто; очень жаль, что итератор, от которого вы получаете itertools.takewhile(), использует StopIteration и как маркер конца группы и как то, что вы получаете в конце файла. Что еще хуже, я не могу найти способ спросить объект-итератор файла, достиг ли он конца файла или нет. И что еще хуже, itertools.takewhile(), кажется, мгновенно переводит файловый итератор в конец файла; когда я попытался переписать вышеприведенное, чтобы проверить наш прогресс, используя lines.tell(), он уже был в конце файла после первой группы.

Я предлагаю использовать решение itertools.groupby(). Это чище.

1 голос
/ 02 сентября 2011

itertools.takewhile неявно выполняет итерации по lines файла, чтобы захватить куски, но так же for line in lines:.Каждый раз в цикле line захватывается, выбрасывается (поскольку нет кода, использующего line), а затем еще несколько block редактируются вместе.

1 голос
/ 02 сентября 2011

Я думаю, проблема в том, что вы принимаете lines в своей лямбда-функции, а не line.Каков ваш ожидаемый результат?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...