(Python)尽可能快地计算一个巨大(> 10GB)文件中的行数[重复]

Posted

技术标签:

【中文标题】(Python)尽可能快地计算一个巨大(> 10GB)文件中的行数[重复]【英文标题】:(Python) Counting lines in a huge (>10GB) file as fast as possible [duplicate] 【发布时间】:2012-03-26 14:47:01 【问题描述】:

我现在有一个非常简单的脚本,它使用 enumerate() 计算文本文件中的行数:

i = 0
f = open("C:/Users/guest/Desktop/file.log", "r")
for i, line in enumerate(f):
      pass
print i + 1
f.close()

浏览一个 15GB 的日志文件大约需要 3 分半钟,其中包含约 3000 万行。如果我能在两分钟或更短的时间内完成它会很棒,因为这些是每日日志,我们希望每月进行一次分析,所以代码将不得不处理 30 个约 15GB 的日志 - 可能超过一个半小时,并且我们希望最大限度地减少服务器上的时间和内存负载。

我也会满足于一个好的近似/估计方法,但它需要大约 4 sig fig 准确...

谢谢!

【问题讨论】:

一般来说,将文件视为二进制数据,以合理大小的块(例如,一次 4KB)读取文件并计算每个块中的 \n 字符可能会更快随你去。 这并不比你幼稚的解决方案表现更好,但供你参考的pythonic方式来写你在这里所拥有的只是with open(fname) as f: print sum(1 for line in f) aroth:感谢您的提示,我应该调查一下。 wim:太好了,谢谢,这要短得多... 看看rawbigcount Michael Bacon 的回答。可能对你有帮助! 【参考方案1】:

Ignacio's answer 是正确的,但如果您使用 32 位进程,则可能会失败。

但也许按块读取文件然后计算每个块中的\n 字符可能会很有用。

def blocks(files, size=65536):
    while True:
        b = files.read(size)
        if not b: break
        yield b

with open("file", "r") as f:
    print sum(bl.count("\n") for bl in blocks(f))

会做你的工作。

请注意,我不会以二进制形式打开文件,因此\r\n 将转换为\n,使计数更可靠。

对于 Python 3,并使其更加健壮,用于读取具有各种字符的文件:

def blocks(files, size=65536):
    while True:
        b = files.read(size)
        if not b: break
        yield b

with open("file", "r",encoding="utf-8",errors='ignore') as f:
    print (sum(bl.count("\n") for bl in blocks(f)))

【讨论】:

就像一个数据点一样,读取大约 51 MB 的大文件从使用简单方法的大约一分钟到使用这种方法的不到一秒。 @MKatz 现在是“一个大文件”还是“一个大约 51 MB 的文件”? ;-) 此解决方案可能会遗漏最后一行,但这对于大文件可能无关紧要。 @ngọcminh.oss 仅当最后一行不完整时。文本文件定义为以换行符结尾,请参阅pubs.opengroup.org/onlinepubs/9699919799/basedefs/… 和***.com/a/729795/296974。 并不是人们关心定义。当您使用真实数据时,一切都很混乱。不过没关系。【参考方案2】:

我知道这有点不公平,但你可以这样做

int(subprocess.check_output("wc -l C:\\alarm.bat").split()[0])

如果您使用的是 Windows,请查看Coreutils。

【讨论】:

我的解决方案只需要 1m37 实时。 这要快得多 看来你需要为python3做int(subprocess.check_output("/usr/bin/wc -l cred", shell=True).split()[0]) 如果您有大文件或很多文件,如果您正在寻找纯粹的性能而不求助于其他语言,请考虑使用这种方法。【参考方案3】:

一种快速的单行解决方案是:

sum(1 for i in open(file_path, 'rb'))

它应该适用于任意大小的文件。

【讨论】:

我确认这是最快的(wc -l hack 除外)。使用文本模式在性能上会有所下降,但与其他解决方案相比是微不足道的。 有一个不需要的额外生成器括号,顺便说一句。 如果没有多余的生成器括号,它似乎会稍微快一些(每 timeit)并且消耗大约 3MB 的内存(对于 100,000 行的文件,每 memit).. 如果文件是带有换行符的文本文件,似乎不起作用。我的问题是需要字符计数的大 txt 文件。 文件没有关闭。【参考方案4】:

mmap 文件,并计算换行符。

【讨论】:

请考虑添加一个简短的示例来演示这一点,谢谢! 简短的例子可能是个好主意,我同意【参考方案5】:

我会扩展 gl 的答案并使用多处理 Python 模块运行他/她的代码以加快计数:

def blocks(f, cut, size=64*1024): # 65536
    start, chunk =cut
    iter=0
    read_size=int(size)
    _break =False
    while not _break:
        if _break: break
        if f.tell()+size>start+chunk:
            read_size=int(start+chunk- f.tell() )
            _break=True
        b = f.read(read_size)
        iter +=1
        if not b: break
        yield b


def get_chunk_line_count(data):
    fn,  chunk_id, cut = data
    start, chunk =cut
    cnt =0
    last_bl=None

    with open(fn, "r") as f:
        if 0:
            f.seek(start)
            bl = f.read(chunk)
            cnt= bl.count('\n')
        else:
            f.seek(start)
            for i, bl  in enumerate(blocks(f,cut)):
                cnt +=  bl.count('\n')
                last_bl=bl

        if not last_bl.endswith('\n'):
            cnt -=1

        return cnt
....
pool = multiprocessing.Pool(processes=pool_size,
                            initializer=start_process,
                            )
pool_outputs = pool.map(get_chunk_line_count, inputs)
pool.close() # no more tasks
pool.join() 

这将使计数性能提高 20 倍。 我把它打包到 script 并放到 Github 上。

【讨论】:

@olekb 感谢您分享多处理方法。作为新手的快速问题,我们如何运行这段代码来计算大文件中的一行(比如,'myfile.txt')?我是否尝试过pool = multiprocessing.Pool(4); pool_outputs = pool.map(get_chunk_line_count, 'myfile.txt'),但这会导致错误。提前感谢您的回答!

以上是关于(Python)尽可能快地计算一个巨大(> 10GB)文件中的行数[重复]的主要内容,如果未能解决你的问题,请参考以下文章

需要尽可能快地反向迭代 Python 列表

Windows科学计算器与Python,计算结果的巨大差异?

Node JS worker - 需要他们吗?

尽可能快地获取我的 MySQL 的第一行(也在大表上)

我想强制渲染,但尽可能快地绘制(InvalidateVisual / CompositionTarget.Rendering)

在 C# 中尽可能快地将数组复制到结构数组