如何在Python中廉价地获得行数?

时间:2009-05-10 10:22:06

标签: python text-files line-count

我需要在python中获取大文件(数十万行)的行数。记忆和时间方面最有效的方法是什么?

目前我这样做:

def file_len(fname):
    with open(fname) as f:
        for i, l in enumerate(f):
            pass
    return i + 1

可以做得更好吗?

43 个答案:

答案 0 :(得分:530)

一行,可能非常快:

num_lines = sum(1 for line in open('myfile.txt'))

答案 1 :(得分:301)

你不能比这更好。

毕竟,任何解决方案都必须读取整个文件,找出你有多少\n,然后返回结果。

在没有阅读整个文件的情况下,您有更好的方法吗?不确定......最好的解决方案永远都是I / O限制,你能做的最好就是确保你不要使用不必要的内存,但看起来你已经覆盖了它。

答案 2 :(得分:189)

我相信内存映射文件将是最快的解决方案。我尝试了四个函数:OP发布的函数(opcount);对文件中的行进行简单迭代(simplecount);具有内存映射字段(mmap)(mapcount)的readline;以及Mykola Kharechko(bufcount)提供的缓冲读取解决方案。

我运行了五次每个函数,并计算了一个120万行文本文件的平均运行时间。

Windows XP,Python 2.5,2GB RAM,2 GHz AMD处理器

以下是我的结果:

mapcount : 0.465599966049
simplecount : 0.756399965286
bufcount : 0.546800041199
opcount : 0.718600034714

编辑:Python 2.6的数字:

mapcount : 0.471799945831
simplecount : 0.634400033951
bufcount : 0.468800067902
opcount : 0.602999973297

因此缓冲区读取策略似乎是Windows / Python 2.6中最快的

以下是代码:

from __future__ import with_statement
import time
import mmap
import random
from collections import defaultdict

def mapcount(filename):
    f = open(filename, "r+")
    buf = mmap.mmap(f.fileno(), 0)
    lines = 0
    readline = buf.readline
    while readline():
        lines += 1
    return lines

def simplecount(filename):
    lines = 0
    for line in open(filename):
        lines += 1
    return lines

def bufcount(filename):
    f = open(filename)                  
    lines = 0
    buf_size = 1024 * 1024
    read_f = f.read # loop optimization

    buf = read_f(buf_size)
    while buf:
        lines += buf.count('\n')
        buf = read_f(buf_size)

    return lines

def opcount(fname):
    with open(fname) as f:
        for i, l in enumerate(f):
            pass
    return i + 1


counts = defaultdict(list)

for i in range(5):
    for func in [mapcount, simplecount, bufcount, opcount]:
        start_time = time.time()
        assert func("big_file.txt") == 1209138
        counts[func].append(time.time() - start_time)

for key, vals in counts.items():
    print key.__name__, ":", sum(vals) / float(len(vals))

答案 3 :(得分:99)

我必须在类似的问题上发布这个问题,直到我的声望得分有所提高(感谢任何撞到我的人!)。

所有这些解决方案都忽略了一种方法,使运行速度更快,即使用无缓冲(原始)接口,使用bytearrays,并进行自己的缓冲。 (这仅适用于Python 3.在Python 2中,默认情况下可能使用或不使用原始接口,但在Python 3中,您将默认使用Unicode。)

使用修改版的计时工具,我相信以下代码比任何提供的解决方案更快(并且更加pythonic):

def rawcount(filename):
    f = open(filename, 'rb')
    lines = 0
    buf_size = 1024 * 1024
    read_f = f.raw.read

    buf = read_f(buf_size)
    while buf:
        lines += buf.count(b'\n')
        buf = read_f(buf_size)

    return lines

使用单独的生成器功能,可以更快地运行:

def _make_gen(reader):
    b = reader(1024 * 1024)
    while b:
        yield b
        b = reader(1024*1024)

def rawgencount(filename):
    f = open(filename, 'rb')
    f_gen = _make_gen(f.raw.read)
    return sum( buf.count(b'\n') for buf in f_gen )

这可以使用itertools在线生成器表达式完全完成,但看起来非常奇怪:

from itertools import (takewhile,repeat)

def rawincount(filename):
    f = open(filename, 'rb')
    bufgen = takewhile(lambda x: x, (f.raw.read(1024*1024) for _ in repeat(None)))
    return sum( buf.count(b'\n') for buf in bufgen )

以下是我的时间:

function      average, s  min, s   ratio
rawincount        0.0043  0.0041   1.00
rawgencount       0.0044  0.0042   1.01
rawcount          0.0048  0.0045   1.09
bufcount          0.008   0.0068   1.64
wccount           0.01    0.0097   2.35
itercount         0.014   0.014    3.41
opcount           0.02    0.02     4.83
kylecount         0.021   0.021    5.05
simplecount       0.022   0.022    5.25
mapcount          0.037   0.031    7.46

答案 4 :(得分:81)

您可以执行子流程并运行wc -l filename

import subprocess

def file_len(fname):
    p = subprocess.Popen(['wc', '-l', fname], stdout=subprocess.PIPE, 
                                              stderr=subprocess.PIPE)
    result, err = p.communicate()
    if p.returncode != 0:
        raise IOError(err)
    return int(result.strip().split()[0])

答案 5 :(得分:39)

这是一个python程序,它使用多处理库来分配跨机器/核心的行数。我的测试使用8核Windows 64服务器改进了计算2000万行文件的时间从26秒到7秒。注意:不使用内存映射会使事情变慢。

import multiprocessing, sys, time, os, mmap
import logging, logging.handlers

def init_logger(pid):
    console_format = 'P{0} %(levelname)s %(message)s'.format(pid)
    logger = logging.getLogger()  # New logger at root level
    logger.setLevel( logging.INFO )
    logger.handlers.append( logging.StreamHandler() )
    logger.handlers[0].setFormatter( logging.Formatter( console_format, '%d/%m/%y %H:%M:%S' ) )

def getFileLineCount( queues, pid, processes, file1 ):
    init_logger(pid)
    logging.info( 'start' )

    physical_file = open(file1, "r")
    #  mmap.mmap(fileno, length[, tagname[, access[, offset]]]

    m1 = mmap.mmap( physical_file.fileno(), 0, access=mmap.ACCESS_READ )

    #work out file size to divide up line counting

    fSize = os.stat(file1).st_size
    chunk = (fSize / processes) + 1

    lines = 0

    #get where I start and stop
    _seedStart = chunk * (pid)
    _seekEnd = chunk * (pid+1)
    seekStart = int(_seedStart)
    seekEnd = int(_seekEnd)

    if seekEnd < int(_seekEnd + 1):
        seekEnd += 1

    if _seedStart < int(seekStart + 1):
        seekStart += 1

    if seekEnd > fSize:
        seekEnd = fSize

    #find where to start
    if pid > 0:
        m1.seek( seekStart )
        #read next line
        l1 = m1.readline()  # need to use readline with memory mapped files
        seekStart = m1.tell()

    #tell previous rank my seek start to make their seek end

    if pid > 0:
        queues[pid-1].put( seekStart )
    if pid < processes-1:
        seekEnd = queues[pid].get()

    m1.seek( seekStart )
    l1 = m1.readline()

    while len(l1) > 0:
        lines += 1
        l1 = m1.readline()
        if m1.tell() > seekEnd or len(l1) == 0:
            break

    logging.info( 'done' )
    # add up the results
    if pid == 0:
        for p in range(1,processes):
            lines += queues[0].get()
        queues[0].put(lines) # the total lines counted
    else:
        queues[0].put(lines)

    m1.close()
    physical_file.close()

if __name__ == '__main__':
    init_logger( 'main' )
    if len(sys.argv) > 1:
        file_name = sys.argv[1]
    else:
        logging.fatal( 'parameters required: file-name [processes]' )
        exit()

    t = time.time()
    processes = multiprocessing.cpu_count()
    if len(sys.argv) > 2:
        processes = int(sys.argv[2])
    queues=[] # a queue for each process
    for pid in range(processes):
        queues.append( multiprocessing.Queue() )
    jobs=[]
    prev_pipe = 0
    for pid in range(processes):
        p = multiprocessing.Process( target = getFileLineCount, args=(queues, pid, processes, file_name,) )
        p.start()
        jobs.append(p)

    jobs[0].join() #wait for counting to finish
    lines = queues[0].get()

    logging.info( 'finished {} Lines:{}'.format( time.time() - t, lines ) )

答案 6 :(得分:11)

我会使用Python的文件对象方法readlines,如下所示:

with open(input_file) as foo:
    lines = len(foo.readlines())

这将打开文件,在文件中创建一个行列表,计算列表的长度,将其保存到变量并再次关闭文件。

答案 7 :(得分:9)

def file_len(full_path):
  """ Count number of lines in a file."""
  f = open(full_path)
  nr_of_lines = sum(1 for line in f)
  f.close()
  return nr_of_lines

答案 8 :(得分:9)

这是我使用的,看起来很干净:

import subprocess

def count_file_lines(file_path):
    """
    Counts the number of lines in a file using wc utility.
    :param file_path: path to file
    :return: int, no of lines
    """
    num = subprocess.check_output(['wc', '-l', file_path])
    num = num.split(' ')
    return int(num[0])

更新:这比使用纯python略快,但是以内存使用为代价。在执行命令时,子进程将使用与父进程相同的内存占用分叉新进程。

答案 9 :(得分:8)

Kyle's answer

num_lines = sum(1 for line in open('my_file.txt'))

可能是最好的,替代方案是

num_lines =  len(open('my_file.txt').read().splitlines())

以下是两者性能的比较

In [20]: timeit sum(1 for line in open('Charts.ipynb'))
100000 loops, best of 3: 9.79 µs per loop

In [21]: timeit len(open('Charts.ipynb').read().splitlines())
100000 loops, best of 3: 12 µs per loop

答案 10 :(得分:8)

一线解决方案

import os
os.system("wc -l  filename")  

我的代码段

  
    
      

os.system(&#39; wc -l * .txt&#39;)

    
  
0 bar.txt
1000 command.txt
3 test_file.txt
1003 total

答案 11 :(得分:8)

我在这个版本中获得了一个小的(4-8%)改进,它重新使用了一个常量缓冲区,所以它应该避免任何内存或GC开销:

lines = 0
buffer = bytearray(2048)
with open(filename) as f:
  while f.readinto(buffer) > 0:
      lines += buffer.count('\n')

你可以使用缓冲区大小,也许可以看到一些改进。

答案 12 :(得分:7)

使用现代subprocess.check_output函数的类似于this answer的单行bash解决方案:

def line_count(file):
    return int(subprocess.check_output('wc -l {}'.format(file), shell=True).split()[0])

答案 13 :(得分:6)

此代码更短更清晰。这可能是最好的方式:

num_lines = open('yourfile.ext').read().count('\n')

答案 14 :(得分:6)

这是我用纯python发现的最快的东西。 您可以通过设置缓冲区来使用任何数量的内存,但2 ** 16似乎是我计算机上的最佳位置。

from functools import partial

buffer=2**16
with open(myfile) as f:
        print sum(x.count('\n') for x in iter(partial(f.read,buffer), ''))

我在这里找到答案Why is reading lines from stdin much slower in C++ than Python?并稍微调整一下。阅读它是一个非常好的阅读,以便快速了解如何计算行数,但wc -l仍然比其他任何内容快75%。

答案 15 :(得分:5)

为了完成上述方法,我尝试了一个带有fileinput模块的变体:

import fileinput as fi   
def filecount(fname):
        for line in fi.input(fname):
            pass
        return fi.lineno()

并将60mil行文件传递给上述所有方法:

mapcount : 6.1331050396
simplecount : 4.588793993
opcount : 4.42918205261
filecount : 43.2780818939
bufcount : 0.170812129974

令我感到有点意外的是,fileinput很糟糕,并且比其他所有方法都要糟糕得多......

答案 16 :(得分:4)

print open('file.txt', 'r').read().count("\n") + 1

答案 17 :(得分:4)

至于我,这个变种将是最快的:

#!/usr/bin/env python

def main():
    f = open('filename')                  
    lines = 0
    buf_size = 1024 * 1024
    read_f = f.read # loop optimization

    buf = read_f(buf_size)
    while buf:
        lines += buf.count('\n')
        buf = read_f(buf_size)

    print lines

if __name__ == '__main__':
    main()

原因:缓冲比逐行读取更快,string.count也非常快

答案 18 :(得分:3)

我修改了这样的缓冲区:

def CountLines(filename):
    f = open(filename)
    try:
        lines = 1
        buf_size = 1024 * 1024
        read_f = f.read # loop optimization
        buf = read_f(buf_size)

        # Empty file
        if not buf:
            return 0

        while buf:
            lines += buf.count('\n')
            buf = read_f(buf_size)

        return lines
    finally:
        f.close()

现在也是空文件,最后一行(没有\ n)被计算在内。

答案 19 :(得分:3)

经过perfplot分析后,不得不推荐缓冲读取方案

def buf_count_newlines_gen(fname):
    def _make_gen(reader):
        b = reader(2 ** 16)
        while b:
            yield b
            b = reader(2 ** 16)

    with open(fname, "rb") as f:
        count = sum(buf.count(b"\n") for buf in _make_gen(f.raw.read))
    return count

它速度快且内存效率高。大多数其他解决方案大约慢 20 倍。 Unix 命令行工具 wc 也做得很好,但只适用于非常大的文件。

enter image description here


重现情节的代码:

import mmap
import subprocess
from functools import partial

import perfplot


def setup(n):
    fname = "t.txt"
    with open(fname, "w") as f:
        for i in range(n):
            f.write(str(i) + "\n")
    return fname


def for_enumerate(fname):
    i = 0
    with open(fname) as f:
        for i, _ in enumerate(f):
            pass
    return i + 1


def sum1(fname):
    return sum(1 for _ in open(fname))


def mmap_count(fname):
    with open(fname, "r+") as f:
        buf = mmap.mmap(f.fileno(), 0)

    lines = 0
    while buf.readline():
        lines += 1
    return lines


def for_open(fname):
    lines = 0
    for _ in open(fname):
        lines += 1
    return lines


def buf_count_newlines(fname):
    lines = 0
    buf_size = 2 ** 16
    with open(fname) as f:
        buf = f.read(buf_size)
        while buf:
            lines += buf.count("\n")
            buf = f.read(buf_size)
    return lines


def buf_count_newlines_gen(fname):
    def _make_gen(reader):
        b = reader(2 ** 16)
        while b:
            yield b
            b = reader(2 ** 16)

    with open(fname, "rb") as f:
        count = sum(buf.count(b"\n") for buf in _make_gen(f.raw.read))
    return count


def wc_l(fname):
    return int(subprocess.check_output(["wc", "-l", fname]).split()[0])


def sum_partial(fname):
    with open(fname) as f:
        count = sum(x.count("\n") for x in iter(partial(f.read, 2 ** 16), ""))
    return count


def read_count(fname):
    return open(fname).read().count("\n")


b = perfplot.bench(
    setup=setup,
    kernels=[
        for_enumerate,
        sum1,
        mmap_count,
        for_open,
        wc_l,
        buf_count_newlines,
        buf_count_newlines_gen,
        sum_partial,
        read_count,
    ],
    n_range=[2 ** k for k in range(27)],
    xlabel="num lines",
)
b.save("out.png")
b.show()

答案 20 :(得分:3)

打开文件的结果是迭代器,可以将其转换为序列,其长度为:

with open(filename) as f:
   return len(list(f))

这比显式循环更简洁,并且避免使用enumerate

答案 21 :(得分:3)

简单方法:

num_lines = len(list(open('myfile.txt')))

答案 22 :(得分:2)

这个怎么样?

def file_len(fname):
  counts = itertools.count()
  with open(fname) as f: 
    for _ in f: counts.next()
  return counts.next()

答案 23 :(得分:2)

count = max(enumerate(open(filename)))[0]

答案 24 :(得分:2)

如果有人想在Linux中用Python廉价地获得行数,我推荐这种方法:

import os
print os.popen("wc -l file_path").readline().split()[0]

file_path既可以是抽象文件路径,也可以是相对路径。希望这可能有所帮助。

答案 25 :(得分:1)

这是对其他一些答案的元注释。

  • 行读取和缓冲\n计数技术不会为每个文件返回相同的答案,因为某些文本文件在最后一行的末尾没有换行符。您可以通过检查最后一个非空缓冲区的最后一个字节并加1(如果不是b'\n'来解决此问题)。

  • 在Python 3中,以文本模式和二进制模式打开文件会产生不同的结果,因为默认情况下,文本模式会将CR,LF和CRLF识别为行尾(将它们全部转换为'\n' ),而在二进制模式下,如果您计算b'\n',则仅计算LF和CRLF。无论是按行读取还是读取固定大小的缓冲区,这都适用。经典的Mac OS使用CR作为行尾。我不知道这些天这些文件有多普遍。

  • 缓冲区读取方法使用与文件大小无关的有限RAM,而行读取方法可以在最坏的情况下一次将整个文件读入RAM(尤其是如果文件使用CR行尾) )。在最坏的情况下,由于动态调整行缓冲区的大小以及(如果以文本模式打开)Unicode解码和存储会产生开销,因此它可能会使用比文件大小大得多的RAM。

  • 通过预分配字节数组并使用readinto而不是read,可以提高缓冲方法的内存使用率,并可能提高速度。现有的答案之一(票数很少)可以做到这一点,但它有很多问题(它重复计算了一些字节)。

  • 最重要的读取缓冲区的答案是使用大缓冲区(1 MiB)。实际上,由于预读操作系统,使用较小的缓冲区实际上可以更快。如果您一次读取32K或64K,则操作系统可能会在您请求之前将下一个32K / 64K读入高速缓存,并且每次访问内核的过程都会立即返回。如果您一次读取1 MiB,则操作系统不太可能以推测方式读取整个兆字节。它可能预读了较少的数据,但您仍然需要花费大量时间坐在内核中,等待磁盘返回其余数据。

答案 26 :(得分:1)

def line_count(path):
    count = 0
    with open(path) as lines:
        for count, l in enumerate(lines, start=1):
            pass
    return count

答案 27 :(得分:1)

这个单行怎么样:

file_length = len(open('myfile.txt','r').read().split('\n'))

使用此方法花费0.003秒在3900行文件上计时

def c():
  import time
  s = time.time()
  file_length = len(open('myfile.txt','r').read().split('\n'))
  print time.time() - s

答案 28 :(得分:1)

这个怎么样?

import fileinput
import sys

counter=0
for line in fileinput.input([sys.argv[1]]):
    counter+=1

fileinput.close()
print counter

答案 29 :(得分:0)

另一种可能性:

import subprocess

def num_lines_in_file(fpath):
    return int(subprocess.check_output('wc -l %s' % fpath, shell=True).strip().split()[0])

答案 30 :(得分:0)

您可以通过以下方式使用os.path模块:

import os
import subprocess
Number_lines = int( (subprocess.Popen( 'wc -l {0}'.format( Filename ), shell=True, stdout=subprocess.PIPE).stdout).readlines()[0].split()[0] )

,其中Filename是文件的绝对路径。

答案 31 :(得分:0)

Exception in thread "AWT-EventQueue-0" java.lang.ExceptionInInitializerError
at net.sf.jasperreports.engine.fill.JRBaseFiller.<init>(JRBaseFiller.java:124)
at net.sf.jasperreports.engine.fill.JRVerticalFiller.<init>(JRVerticalFiller.java:89)
at net.sf.jasperreports.engine.fill.JRVerticalFiller.<init>(JRVerticalFiller.java:104)
at net.sf.jasperreports.engine.fill.JRVerticalFiller.<init>(JRVerticalFiller.java:62)
at net.sf.jasperreports.engine.fill.JRFiller.createFiller(JRFiller.java:179)
at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:108)
at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:668)
at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:984)
at cop.JRep.PrintActionPerformed(JRep.java:845)
at cop.JRep.access$1300(JRep.java:55)
at cop.JRep$14.actionPerformed(JRep.java:474)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.AbstractButton.doClick(Unknown Source)
at javax.swing.AbstractButton.doClick(Unknown Source)
at cop.JRep.jButton3ActionPerformed(JRep.java:797)
at cop.JRep.access$700(JRep.java:55)
at cop.JRep$8.actionPerformed(JRep.java:324)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: net.sf.jasperreports.engine.JRRuntimeException: net.sf.jasperreports.engine.JRException: Input stream not found at : net/sf/jasperreports/fonts/fonts.xml
    at net.sf.jasperreports.engine.fonts.SimpleFontExtensionHelper.loadFontFamilies(SimpleFontExtensionHelper.java:188)
    at net.sf.jasperreports.engine.fonts.FontExtensionsRegistry.getExtensions(FontExtensionsRegistry.java:56)
    at net.sf.jasperreports.extensions.DefaultExtensionsRegistry.getExtensions(DefaultExtensionsRegistry.java:110)
    at net.sf.jasperreports.engine.util.JRStyledTextParser.<clinit>(JRStyledTextParser.java:83)
    ... 56 more
Caused by: net.sf.jasperreports.engine.JRException: Input stream not found at : net/sf/jasperreports/fonts/fonts.xml
    at net.sf.jasperreports.repo.RepositoryUtil.getInputStreamFromLocation(RepositoryUtil.java:159)
    at net.sf.jasperreports.engine.fonts.SimpleFontExtensionHelper.loadFontFamilies(SimpleFontExtensionHelper.java:183)
    ... 59 more

答案 32 :(得分:0)

如果文件可以放入内存,那么

with open(fname) as f:
    count = len(f.read().split(b'\n')) - 1

答案 33 :(得分:0)

创建一个名为count.py的可执行脚本文件:

#!/usr/bin/python

import sys
count = 0
for line in sys.stdin:
    count+=1

,然后将文件内容通过管道传递到python脚本:cat huge.txt | ./count.py。管道也可以在Powershell上运行,因此您最终将要计算行数。

对我来说,在Linux上,速度比以下方法快30%:

count=1
with open('huge.txt') as f:
    count+=1

答案 34 :(得分:0)

如果文件中的所有行都具有相同的长度(并且仅包含ASCII字符)*,则可以非常便宜地执行以下操作:

fileSize     = os.path.getsize( pathToFile )  # file size in bytes
bytesPerLine = someInteger                    # don't forget to account for the newline character
numLines     = fileSize // bytesPerLine

*我怀疑如果使用é之类的Unicode字符,将需要更多的努力来确定一行中的字节数。

答案 35 :(得分:0)

大文件的替代方法是使用xreadlines():

...

答案 36 :(得分:0)

使用Numba

我们可以使用Numba进行JIT(及时)将我们的函数编译为机器代码。 def numbacountparallel(fname)运行速度快2.8倍 而不是问题中的def file_len(fname)。

注意:

在运行基准测试之前,操作系统已经将文件缓存到内存中,因为我在PC上看不到太多磁盘活动。 第一次读取文件时,时间会慢得多,这使得使用Numba的时间优势微不足道。

JIT编译在第一次调用该函数时会花费额外的时间。

如果我们要做的不仅仅是计数行数,这将非常有用。

Cython是另一种选择。

http://numba.pydata.org/

结论

由于计数行将受IO约束,因此,除非要进行除计数行之外的其他操作,否则请使用问题中的def file_len(fname)。

import timeit

from numba import jit, prange
import numpy as np

from itertools import (takewhile,repeat)

FILE = '../data/us_confirmed.csv' # 40.6MB, 371755 line file
CR = ord('\n')


# Copied from the question above. Used as a benchmark
def file_len(fname):
    with open(fname) as f:
        for i, l in enumerate(f):
            pass
    return i + 1


# Copied from another answer. Used as a benchmark
def rawincount(filename):
    f = open(filename, 'rb')
    bufgen = takewhile(lambda x: x, (f.read(1024*1024*10) for _ in repeat(None)))
    return sum( buf.count(b'\n') for buf in bufgen )


# Single thread
@jit(nopython=True)
def numbacountsingle_chunk(bs):

    c = 0
    for i in range(len(bs)):
        if bs[i] == CR:
            c += 1

    return c


def numbacountsingle(filename):
    f = open(filename, "rb")
    total = 0
    while True:
        chunk = f.read(1024*1024*10)
        lines = numbacountsingle_chunk(chunk)
        total += lines
        if not chunk:
            break

    return total


# Multi thread
@jit(nopython=True, parallel=True)
def numbacountparallel_chunk(bs):

    c = 0
    for i in prange(len(bs)):
        if bs[i] == CR:
            c += 1

    return c


def numbacountparallel(filename):
    f = open(filename, "rb")
    total = 0
    while True:
        chunk = f.read(1024*1024*10)
        lines = numbacountparallel_chunk(np.frombuffer(chunk, dtype=np.uint8))
        total += lines
        if not chunk:
            break

    return total

print('numbacountparallel')
print(numbacountparallel(FILE)) # This allows Numba to compile and cache the function without adding to the time.
print(timeit.Timer(lambda: numbacountparallel(FILE)).timeit(number=100))

print('\nnumbacountsingle')
print(numbacountsingle(FILE))
print(timeit.Timer(lambda: numbacountsingle(FILE)).timeit(number=100))

print('\nfile_len')
print(file_len(FILE))
print(timeit.Timer(lambda: rawincount(FILE)).timeit(number=100))

print('\nrawincount')
print(rawincount(FILE))
print(timeit.Timer(lambda: rawincount(FILE)).timeit(number=100))

每个功能调用100次的时间(以秒为单位)

numbacountparallel
371755
2.8007332000000003

numbacountsingle
371755
3.1508585999999994

file_len
371755
6.7945494

rawincount
371755
6.815438

答案 37 :(得分:0)

已经有很多答案了,但是不幸的是,大多数答案只是很小的经济体,几乎没有一个可乐观的问题……

我参与了多个项目,其中行数是软件的核心功能,并且尽可能快地处理大量文件是至关重要的。

行数的主要瓶颈是I / O访问,因为您需要读取每一行以检测行返回字符,所以根本无法解决。第二个潜在的瓶颈是内存管理:一次加载越多,处理速度就越快,但是与第一个瓶颈相比,可以忽略不计。

因此,除了禁用gc收集和其他微管理技巧之类的微小优化之外,还有3种主要方法可以减少行计数函数的处理时间:

  1. 硬件解决方案:主要且最明显的方法是非编程方式:购买非常快的SSD /闪存硬盘。到目前为止,这是最大的速度提升方法。

  2. 数据准备解决方案:如果您生成或可以修改处理文件的生成方式,或者可以对其进行预处理是可以接受的,请首先将行转换为unix样式(\n),因为与Windows或MacOS样式相比,这将节省1个字符(不是很大的节省,但是很容易获得),其次,也是最重要的是,您可以编写固定长度的行。如果需要可变长度,则始终可以填充较小的行。这样,您可以立即从总文件大小中计算行数,这可以更快地访问。通常,对问题的最佳解决方案是对其进行预处理,以使其更适合最终目的。

  3. 并行化+硬件解决方案::如果您可以购买多个硬盘(如果可能的话,还可以购买SSD闪存盘),那么通过并行化,您甚至可以超越一个磁盘的速度,在磁盘之间以平衡方式存储文件(最简单的方法是按总大小平衡),然后从所有这些磁盘并行读取。然后,您可以期望获得与磁盘数量成比例的乘数提升。如果您不打算购买多个磁盘,那么并行化可能无济于事(除非您的磁盘具有多个读取标头,例如某些专业级磁盘,但即使如此,该磁盘的内部高速缓存存储器和PCB电路也可能成为瓶颈。并阻止您完全并行使用所有磁头,此外,您还必须为此硬盘设计一个特定的代码,因为您需要知道确切的群集映射,以便将文件存储在不同磁头下的群集中,等等。之后您可以用不同的头阅读它们)。确实,众所周知,顺序读取几乎总是比随机读取更快,并且单个磁盘上的并行化性能将比顺序读取更类似于随机读取(例如,您可以使用CrystalDiskMark在两个方面测试硬盘速度)

如果这些都不是一个选项,那么您只能依靠微管理技巧将行计数功能的速度提高百分之几,但不要指望任何真正重要的事情。相反,您可以预期,与花在提高速度上的回报相比,花在调整上的时间会成比例。

答案 38 :(得分:0)

我会使用的最简单和最短的方法是:

f = open("my_file.txt", "r")
len(f.readlines())

答案 39 :(得分:-1)

类似地:

lines = 0
with open(path) as f:
    for line in f:
        lines += 1

答案 40 :(得分:-1)

为什么以下不起作用?

import sys

# input comes from STDIN
file = sys.stdin
data = file.readlines()

# get total number of lines in file
lines = len(data)

print lines

在这种情况下,len函数使用输入行作为确定长度的方法。

答案 41 :(得分:-1)

这是怎么回事?

import sys
sys.stdin=open('fname','r')
data=sys.stdin.readlines()
print "counted",len(data),"lines"

答案 42 :(得分:-1)

为什么不读取前100行和后100行并估计平均行长度,然后将总文件大小除以这些数字?如果您不需要确切的值,这可能会有效。