(Python)尽可能快地计算大型(>;10GB)文件中的行数

(Python)尽可能快地计算大型(>;10GB)文件中的行数,python,enumerate,line-count,Python,Enumerate,Line Count,我现在有一个非常简单的脚本,它使用enumerate()计算文本文件中的行数: 这需要大约3.5分钟来浏览一个15GB的日志文件,其中包含约3000万行。如果我能在两分钟或更短的时间内完成,那就太好了,因为这些是每日日志,我们希望每月进行一次分析,因此代码必须处理30个~15GB的日志-可能超过一个半小时,我们希望将服务器上的时间和内存负载降至最低 我也会满足于一个很好的近似/估计方法,但它需要大约4sig的精确度 谢谢大家! 读取文件,并计算换行符。是正确的,但如果使用32位进程,则可能会失败

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

这需要大约3.5分钟来浏览一个15GB的日志文件,其中包含约3000万行。如果我能在两分钟或更短的时间内完成,那就太好了,因为这些是每日日志,我们希望每月进行一次分析,因此代码必须处理30个~15GB的日志-可能超过一个半小时,我们希望将服务器上的时间和内存负载降至最低

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

谢谢大家!

读取文件,并计算换行符。

是正确的,但如果使用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)))

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

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

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

一个快速的单线解决方案是:

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

它可以处理任意大小的文件。

我会扩展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倍。

我将其包装到a并将其放入Github。

一般来说,将文件作为二进制数据处理,以合理大小的块(例如,每次4KB)读取,并在运行时计算每个块中的
\n
字符可能会更快。这并没有比您的原始解决方案更好的性能,但是,仅供参考,用python的方式来编写您在这里的内容只需使用open(fname)作为f:print sum(f中的行为1)aroth:谢谢你的提示,我应该研究一下。维姆:太好了,谢谢,这要短得多……看看迈克尔·培根的答案。这可能对你有帮助!我的解决方案只需要1m37实时。这是非常快的,就像你需要对python3执行
int(subprocess.check\u输出(“/usr/bin/wc-l cred”,shell=True)。split()[0])
,如果你有大文件或很多文件,请考虑使用这种方法,如果你正在寻找纯粹的性能而不诉诸另一种语言。作为一个数据点,一个大约51 MB的大文件的读取从大约一分钟使用天真的方法使用这个方法下一秒。@ MkATZ现在,“一个大文件”或“大约51 MB的文件”?此解决方案可能会漏掉最后一行,但对于大型文件来说,这可能并不重要。@ngọ仅当最后一行不完整时才使用cminh.oss。文本文件定义为以换行符结尾,请参见和。人们并不关心定义。处理真实数据时,一切都很混乱。但是这没关系。请考虑添加一个简短的例子来证明这一点,谢谢!简短的例子可能是个好主意,我同意我确认这是最快的一个(除了
wc-l
hack)。使用文本模式在性能上会有一点下降,但与其他解决方案相比,这是微不足道的。顺便说一句,有一个不需要的额外生成器括号。如果没有不需要的额外生成器括号,它似乎要快得多(每timeit),并消耗大约3MB内存(对于100000行的文件,每个memit)…如果文件是带有换行符的文本文件,则似乎不起作用。我的问题是需要字符计数的大型txt文件。@olekb感谢您分享多处理方法。作为一个新手,我们如何运行这段代码来计算一个大文件中的一行(比如说,'myfile.txt')?我是否尝试了
pool=multiprocessing.pool(4);pool\u outputs=pool.map(获取\u chunk\u line\u count,'myfile.txt')
,但这会导致错误。提前感谢您的回答!
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()