【问题标题】:Fastest way to re-read a file in Python?在 Python 中重新读取文件的最快方法是什么?
【发布时间】:2015-01-23 11:31:32
【问题描述】:

我有一个文件,其中包含名称列表及其位置(开始 - 结束)。

我的脚本遍历该文件,并根据名称读取另一个带有信息的文件,以检查该行是否在这些位置之间,然后从中计算出一些东西。

目前它会逐行读取整个第二个文件(60MB),检查它是否在开始/结束之间。对于第一个列表中的每个名称(大约 5000 个)。收集这些参数之间的数据而不是重新读取整个文件 5000 次的最快方法是什么?

第二个循环的示例代码:

for line in file:
    if int(line.split()[2]) >= start and int(line.split()[2]) <= end:
        Dosomethingwithline():

编辑:将文件加载到第一个循环上方的列表中并对其进行迭代提高了速度。

with open("filename.txt", 'r') as f:
    file2 = f.readlines()
for line in file:
    [...]
    for line2 in file2:
       [...]

【问题讨论】:

  • 为什么不将行加载到列表中并在内存中操作呢? 60Mb 不算多。
  • 啊,是的,我会试试的!

标签: python performance file


【解决方案1】:

您可以使用mmap module 将该文件加载到内存中,然后进行迭代。

例子:

import mmap

# write a simple example file
with open("hello.txt", "wb") as f:
    f.write(b"Hello Python!\n")

with open("hello.txt", "r+b") as f:
    # memory-map the file, size 0 means whole file
    mm = mmap.mmap(f.fileno(), 0)
    # read content via standard file methods
    print(mm.readline())  # prints b"Hello Python!\n"
    # read content via slice notation
    print(mm[:5])  # prints b"Hello"
    # update content using slice notation;
    # note that new content must have same size
    mm[6:] = b" world!\n"
    # ... and read again using standard file methods
    mm.seek(0)
    print(mm.readline())  # prints b"Hello  world!\n"
    # close the map
    mm.close()

【讨论】:

  • 这比将其加载到列表中更快吗? with open("filename, 'r') as f: file2 = f.readlines()
  • 不确定。您可以尝试使用 timeit 模块进行测试。
  • 当一个简单的readlines 足以将所有内容加载到可以简单迭代的列表中时,我无法想象使用内存映射的充分理由......
  • 可能这个问题不是整体问题描述的最佳解决方案,但它解决了 OP 对问题的原始看法,如问题标题中所述。并且很好的总结了mmap的使用方法。谢谢!
  • ...我的意思是'可能是这个答案(...)',当然:)
【解决方案2】:

也许换个循环?将文件迭代设为外循环,将名称列表迭代设为内循环。

name_and_positions = [
    ("name_a", 10, 45),
    ("name_b", 2, 500),
    ("name_c", 96, 243),
]

with open("somefile.txt") as f:
    for line in f:
        value = int(line.split()[2])
        for name, start, end in name_and_positions:
            if start <= value <= end:
                print("matched {} with {}".format(name, value))

【讨论】:

  • 这是一种有趣的方法,但是一个名称有多个匹配项,所以在脚本停止筛选第一个文件之前会有 5000 个变量?位置上也有一些重叠。将文件保存在列表/内存中使其速度更快,所以目前我会坚持下去。不过感谢您的意见!
【解决方案3】:

在我看来,您的问题不在于重读文件,而是将长列表的 slices 与短列表匹配。正如其他答案所指出的,您可以使用普通列表或内存映射文件来加速您的程序。

如果您想使用特定的数据结构来进一步加快速度,那么我建议您查看blist,特别是因为它在切片列表方面比标准 Python 列表具有更好的性能:他们声称 O (log n) 而不是 O(n)

我在 ~10MB 的列表上测量了近 4 倍的加速:

import random

from blist import blist

LINE_NUMBER = 1000000


def write_files(line_length=LINE_NUMBER):
    with open('haystack.txt', 'w') as infile:
        for _ in range(line_length):
            infile.write('an example\n')

    with open('needles.txt', 'w') as infile:
        for _ in range(line_length / 100):
            first_rand = random.randint(0, line_length)
            second_rand = random.randint(first_rand, line_length)
            needle = random.choice(['an example', 'a sample'])
            infile.write('%s\t%s\t%s\n' % (needle, first_rand, second_rand))


def read_files():
    with open('haystack.txt', 'r') as infile:
        normal_list = []
        for line in infile:
            normal_list.append(line.strip())

    enhanced_list = blist(normal_list)
    return normal_list, enhanced_list


def match_over(list_structure):
    matches = 0
    total = len(list_structure)
    with open('needles.txt', 'r') as infile:
        for line in infile:
            needle, start, end = line.split('\t')
            start, end = int(start), int(end)
            if needle in list_structure[start:end]:
                matches += 1
    return float(matches) / float(total)

根据 IPython 的 %time 命令测量,blist 需要 12 秒,而普通的 list 需要 46 秒:

In [1]: import main

In [3]: main.write_files()

In [4]: !ls -lh *.txt
10M haystack.txt
233K needles.txt

In [5]: normal_list, enhanced_list = main.read_files()

In [8]: %time main.match_over(normal_list)
CPU times: user 44.9 s, sys: 1.47 s, total: 46.4 s
Wall time: 46.4 s
Out[8]: 0.005032

In [9]: %time main.match_over(enhanced_list)
CPU times: user 12.6 s, sys: 33.7 ms, total: 12.6 s
Wall time: 12.6 s
Out[9]: 0.005032

【讨论】:

    猜你喜欢
    • 2013-10-24
    • 2013-01-17
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 2011-12-23
    • 2015-05-29
    相关资源
    最近更新 更多