У меня есть файл среднего размера (25 МБ, 1000000 строк), и я хочу читать каждую строку, кроме каждой третьей строки.
FIRST QUESTION: быстрее ли загружать весь файл в память, а затем читать строки (метод .read()
) или загружать и читать одну строку в то время (метод .readline()
)?
Поскольку я не опытный кодер, я попробовал второй вариант с islice
метода itertools
из модуля itertools
.
import intertools
with open(input_file) as inp:
inp_atomtype = itertools.islice(inp, 0, 40, 3)
inp_atomdata = itertools.islice(inp, 1, 40, 3)
for atomtype, atomdata in itertools.zip_longest(inp_atomtype, inp_atomdata):
print(atomtype + atomdata)
Несмотря на то, что цикл через один генератор (inp_atomtype
или inp_atomdata
) печатает правильные данные, циклическое inp_atomdata
обоих из них одновременно (как в этом коде) выводит неверные данные.
ВТОРОЙ ВОПРОС: Как я могу достичь желаемых строк с помощью генераторов?
yield
идеально подходит для этого.
Эти функции дают пары из итерируемого и пропускают каждый третий элемент:
def two_thirds(seq):
_iter = iter(seq)
while True:
yield (next(_iter), next(_iter))
next(_iter)
Вы потеряете половину пар, что означает, что two_thirds(range(2))
немедленно прекратят итерацию.
https://repl.it/repls/DullNecessaryCron
Вы также можете использовать рецепт grouper из документа itertools и игнорировать третий элемент в каждом сгенерированном кортеже:
for atomtype, atomdata, _ in grouper(lines, 3):
pass
Вам не нужно обрезать итератор, достаточно простого счетчика строк:
with open(input_file) as f:
current_line = 0
for line in f:
current_line += 1
if current_line % 3: # ignore every third line
print(line) # NOTE: print() will add an additional new line by default
Что касается превращения его в генератор, просто yield
строку вместо печати.
Когда дело доходит до скорости, учитывая, что вы будете читать свои строки, в любом случае часть ввода-вывода, вероятно, будет иметь то же самое, но вы можете немного выиграть (в общем времени обработки) от быстрого нарезки списка, а не подсчитывать строки, если у вас достаточно рабочая память, чтобы сохранить содержимое файла, и если загрузка всего файла авансом вместо потоковой передачи является приемлемой.
ПЕРВЫЙ ВОПРОС: Я уверен, что.readline() быстрее, чем.read(). Плюс, самый быстрый способ, основанный на моем тестировании, - это сделать lopping как:
with open(file, 'r') as f:
for line in f:
...
ВТОРОЙ ВОПРОС: Я не совсем уверен в этом. вы можете использовать урожай.
Вы можете найти фрагмент кода:
def myreadlines(f, newline):
buf = ""
while True:
while newline in buf:
pos = buf.index(newline)
yield buf[:pos]
buf = buf[pos + len(newline):]
chunk = f.read(4096)
if not chunk:
# the end of file
yield buf
break
buf += chunk
with open("input.txt") as f:
for line in myreadlines(f, "{|}"):
print (line)
Вы можете использовать выражение генератора:
with open(input_file, 'r') as f:
generator = (line for e, line in enumerate(f, start=1) if e % 3)
enumerate
добавляет номера строк в каждую строку, а предложение if
игнорирует номера строк, делящиеся на 3 (нумерация по умолчанию начинается с 0, поэтому вам нужно указать start=1
чтобы получить желаемый шаблон).
Имейте в виду, что вы можете использовать генератор только в том случае, если файл все еще открыт.
q2: здесь мой генератор:
def yield_from_file(input_file):
with open(input_file) as file:
yield from file
def read_two_skip_one(gen):
while True:
try:
val1 = next(gen)
val2 = next(gen)
yield val1, val2
_ = next(gen)
except StopIteration:
break
if __name__ == '__main__':
for atomtype, atomdata in read_two_skip_one(yield_from_file('sample.txt')):
print(atomtype + atomdata)
sample.txt был сгенерирован с помощью оболочки bash (она просто подсчитывает 100)
for i in {001..100}; do echo $i; done > sample.txt
относительно q1: если вы читаете файл несколько раз, вам будет лучше иметь его в памяти. в противном случае вы прекрасно читаете его по строкам.
Что касается проблемы, которую вы испытываете с неправильными результатами:
itertools.islice(inp, 0, 40, 3)
будут использовать inp
как генератор. Оба вызовут next(inp)
, чтобы предоставить вам значение. Каждый раз, когда вы вызываете next()
на итераторе, он меняет свое состояние, так что там, где возникают ваши проблемы.
next(gen)
не назначая его _
atomtype
я хочу правильно обрабатывать переменнуюatomdata
.