Эффективно добавить несколько строк в файл по индексу - PullRequest
0 голосов
/ 29 мая 2019

С учетом входного файла data.dat как этот:

# Some comment
# more comments
#
45.78
# aaa
0.056
0.67
# aaa
345.
0.78
99.
2.34
# aaa
65.7
0.9

Мне нужно добавить разные комментарии над каждой строкой, начинающейся с "# aaa", чтобы она выглядела так:

# Some comment
# more comments
#
45.78
# cmmt1
# aaa
0.056
0.67
# another cmmt
# aaa
345.
0.78
99.
2.34
# last one
# aaa
65.7
0.9

Я априори знаю число комментариев "# aaa", присутствующих в файле data.dat, но не их позиции .

У меня есть способ сделать это (см. Код ниже), но это довольно сложно и совсем не эффективно. Мне нужно применить этот код к сотням больших файлов, поэтому я ищу эффективный способ сделать это.


# Read file
with open("data.dat", mode="r") as f:
    data = f.readlines()

# Indexes of "# aaa" comments
idx = []
for i, line in enumerate(data):
    if line.startswith("# aaa"):
        idx.append(i)

# Insert new comments in their proper positions
add_data = ["# cmmt1\n", "# another cmmt\n", "# last one\n"]
for i, j in enumerate(idx):
    data.insert(j + i, add_data[i])

# Write final data to file
with open("data_final.dat", mode="w") as f:
    for item in data:
        f.write("{}".format(item))

Ответы [ 3 ]

2 голосов
/ 29 мая 2019

Я не делал никаких тестов, но re.sub мог бы быть быстрее - просто загрузите текстовый файл целиком, выполните re.sub и запишите его:

data = '''# Some comment
# more comments
#
45.78
# aaa
0.056
0.67
# aaa
345.
0.78
99.
2.34
# aaa
65.7
0.9'''

import re

def fn():
    add_data = ["# cmmt1\n", "# another cmmt\n", "# last one\n"]
    for d in add_data:
        yield d

out = re.sub(r'^# aaa', lambda r, f=fn(): next(f) + r.group(0), data, flags=re.MULTILINE)
print(out)

Печать:

# Some comment
# more comments
#
45.78
# cmmt1
# aaa
0.056
0.67
# another cmmt
# aaa
345.
0.78
99.
2.34
# last one
# aaa
65.7
0.9

С файлами ввода / вывода:

import re

def fn():
    add_data = ["# cmmt1\n", "# another cmmt\n", "# last one\n"]
    for d in add_data:
        yield d

with open('data.dat', 'r') as f_in, \
    open('data.out', 'w') as f_out:
    f_out.write(re.sub(r'^# aaa', lambda r, f=fn(): next(f) + r.group(0), f_in.read(), flags=re.MULTILINE))

Версия 2:

import re

def fn():
    add_data = ["# cmmt1\n", "# another cmmt\n", "# last one\n"]
    add_data = [s + '#aaa' for s in add_data]
    for d in add_data:
        yield d

with open('data.dat', 'r') as f_in, \
    open('data.out', 'w') as f_out:
    f_out.write(re.sub(r'^# aaa', lambda r, f=fn(): next(f), f_in.read(), flags=re.MULTILINE))
1 голос
/ 29 мая 2019

Когда мне нужно изменить данные в текстовом файле, я пытаюсь прочитать одним дескриптором и сразу же написать вторым.

def add_comments(input_file_name, output_file_name, list_of_comments):
    comments = iter(list_of_comments)  # or itertools.cycle(list_of_comments)
    with open(input_file_name) as fin, open(output_file_name, 'w') as fout:
        for line in fin:
            if line.startswith("# aaa"):
                fout.write(next(comments))
            fout.write(line)

Для вашего примера кода, если будет называться:

add_comments("data.dat", "final_data.dat", ["# cmmt1\n", "# another cmmt\n", "# last one\n"])
1 голос
/ 29 мая 2019

Согласно ответу Джана-Филиппа Герке здесь , вам следует уменьшить количество write вызовов.

Для этого вы можете просто изменить:

with open("data_final.dat", mode="w") as f:
    for item in data:
        f.write("{}".format(item))

до:

with open("data_final.dat", mode="w") as f:
    f.write("".join(data))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...