【问题标题】:Efficiently add multiple lines to file by index按索引有效地将多行添加到文件中
【发布时间】:2019-10-15 03:23:27
【问题描述】:

给定一个像这样的输入文件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”开头的每一行上方添加不同的 cmets,所以它看起来像这样:

# 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

我事先知道data.dat 文件中存在的“# aaa”cmets 的数量,但不知道它们的位置

我有办法做到这一点(见下面的代码),但它相当复杂,而且根本没有效率。我需要将此代码应用于数百个大文件,因此我正在寻找一种有效的方法来执行此操作。


# 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))

【问题讨论】:

    标签: python list performance file-io


    【解决方案1】:

    我没有做任何基准测试,但 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))
    

    【讨论】:

    • 使用data = f.read() 将文件作为字符串读取(以匹配您输入的data 作为字符串)我收到错误消息:TypeError: '_sre.SRE_Match' object has no attribute '__getitem__'
    • @Gabriel 我更新了文件 i/o 的答案。文件data.dat 包含前面示例中的数据。
    • 在 Python 2.7.16 下还是一样的错误,但是在 3.7.2 下没有;猜猜re 模块有什么变化。
    • 我做了一个简单的基准测试,这种方法比我的快 20%。太糟糕了,它不适用于 v2.7,但无论如何谢谢!
    • @Gabriel 对于 python 2.7,只需将 r[0] 替换为 r.group(0)。它应该工作。我更新了我的示例。
    【解决方案2】:

    根据Jan-Philip Gehrcke's response here,你应该减少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))
    

    【讨论】:

    • 这个简单的更改适用于 v2.7 和 v3.x,并且与 Andrej 的回答一样提高了性能。谢谢!
    • 更正:这是 v2.7 下表现最好的,但在 v3.x 下不是(Andrej 的)
    【解决方案3】:

    当我需要更改文本文件中的数据时,我会尝试使用一个句柄读取并立即使用第二个句柄进行写入。

    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"])
    

    【讨论】:

    • 这与 H4kim 的方法一样提高了性能,在 Python v3.x 中大约提高了 6%,但它比 Python 2.7 下的原始方法运行了大约 0.5%(和 Andrej 的方法一样)谢谢!
    • @Gabriel:安德烈为了(一点)速度改变了(很多)内存。请记住,如果您打算处理大文件(接近大于可用内存)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-12
    • 2016-08-03
    • 1970-01-01
    • 2013-07-11
    • 2011-02-16
    • 2016-11-25
    • 1970-01-01
    相关资源
    最近更新 更多