Làm cách nào để có được số lượng dòng của một tệp lớn với giá rẻ trong Python?


1012

Tôi cần lấy số lượng dòng của một tệp lớn (hàng trăm ngàn dòng) trong python. Cách hiệu quả nhất cả về trí nhớ và thời gian là gì?

Hiện tại tôi làm:

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

Có thể làm tốt hơn?


7
Bạn có cần số lượng dòng chính xác hoặc sẽ đủ một xấp xỉ?
pico

43
Tôi sẽ thêm i = -1 trước cho vòng lặp, vì mã này không hoạt động đối với các tệp trống.
Maciek Sawicki

12
@Legend: Tôi cá rằng pico đang suy nghĩ, lấy kích thước tệp (với tìm kiếm (0,2) hoặc tương đương), chia cho độ dài dòng gần đúng. Bạn có thể đọc một vài dòng ở đầu để đoán độ dài dòng trung bình.
Anne

32
enumerate(f, 1)và bỏ mương i + 1?
Ian Mackinnon

4
@IanMackinnon Hoạt động cho các tệp trống, nhưng bạn phải khởi tạo i thành 0 trước vòng lặp for.
scai

Câu trả lời:


357

Bạn không thể nhận được bất kỳ tốt hơn thế.

Rốt cuộc, bất kỳ giải pháp nào cũng sẽ phải đọc toàn bộ tệp, tính xem \nbạn có bao nhiêu và trả về kết quả đó.

Bạn có cách nào tốt hơn để làm điều đó mà không cần đọc toàn bộ tập tin không? Không chắc chắn ... Giải pháp tốt nhất sẽ luôn là ràng buộc I / O, tốt nhất bạn có thể làm là đảm bảo rằng bạn không sử dụng bộ nhớ không cần thiết, nhưng có vẻ như bạn có điều đó.


7
Chính xác, ngay cả WC cũng đang đọc qua tệp, nhưng trong C và nó có thể được tối ưu hóa khá tốt.
Chờ đợi Ólafur

6
Theo như tôi hiểu thì tệp Python IO cũng được thực hiện thông qua C. docs.python.org/l Library / stdtypes.html
Tomalak

9
@Tomalak Đó là một cá trích đỏ. Trong khi python và wc có thể phát hành cùng một tòa nhà, python có opcode gửi qua đầu mà wc không có.
bobpoekert

4
Bạn có thể tính gần đúng số lượng dòng bằng cách lấy mẫu. Nó có thể nhanh hơn hàng ngàn lần. Xem: documentroot.com/2011/02/ Lời
Erik Aronesty

4
Các câu trả lời khác dường như cho thấy câu trả lời phân loại này là sai, và do đó nên được xóa thay vì giữ như được chấp nhận.
Skippy le Grand Gourou

625

Một dòng, có lẽ khá nhanh:

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

8
tương tự như tổng (chuỗi 1) mỗi dòng được tính là 1. >>> [1 cho dòng trong phạm vi (10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] >>> tổng (1 cho dòng trong phạm vi (10)) 10 >>>
James Sapam

4
num_lines = sum (1 cho dòng đang mở ('myfile.txt') if line.rstrip ()) cho bộ lọc các dòng trống
Honghe.Wu

61
khi chúng tôi mở một tệp, điều này sẽ tự động được đóng lại sau khi chúng tôi lặp lại tất cả các yếu tố? Có bắt buộc phải 'đóng ()' không? Tôi nghĩ rằng chúng ta không thể sử dụng 'với open ()' trong câu lệnh ngắn này, phải không?
Mannaggia

16
@Mannaggia bạn đã đúng, sẽ tốt hơn nếu sử dụng 'với open (tên tệp)' để đảm bảo tệp sẽ đóng khi hoàn thành và thậm chí tốt hơn là thực hiện việc này trong một khối ngoại trừ thử, trong đó ngoại lệ và IOError được ném nếu các tập tin không thể được mở.
BoltzmannBrain

17
Một điều cần lưu ý: Đây là ~ 0,04-0,05 giây chậm hơn so với một vấn đề ban đầu đưa vào một tập tin văn bản 300.000 dòng
andrew

202

Tôi tin rằng một tập tin ánh xạ bộ nhớ sẽ là giải pháp nhanh nhất. Tôi đã thử bốn chức năng: chức năng được đăng bởi OP ( opcount); một phép lặp đơn giản trên các dòng trong tệp ( simplecount); đường dẫn với một tập tin ánh xạ bộ nhớ (mmap) ( mapcount); và giải pháp đọc bộ đệm được cung cấp bởi Mykola Kharechko ( bufcount).

Tôi đã chạy từng chức năng năm lần và tính thời gian chạy trung bình cho tệp văn bản 1,2 triệu dòng.

Windows XP, Python 2.5, RAM 2GB, bộ xử lý AMD 2 GHz

Đây là kết quả của tôi:

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

Chỉnh sửa : số cho Python 2.6:

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

Vì vậy, chiến lược đọc bộ đệm dường như là nhanh nhất cho Windows / Python 2.6

Đây là mã:

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

1
Toàn bộ tệp ánh xạ bộ nhớ không được tải vào bộ nhớ. Bạn nhận được một không gian bộ nhớ ảo mà HĐH hoán đổi vào và ra khỏi RAM khi cần thiết. Dưới đây là cách họ xử lý trên Windows: msdn.microsoft.com/en-us/l
Library / ms810613.aspx

1
Xin lỗi, đây là một tài liệu tham khảo tổng quát hơn về các tệp được ánh xạ bộ nhớ: en.wikipedia.org/wiki/Memory-mapped_file Và cảm ơn bạn đã bỏ phiếu. :)
Ryan Ginstrom

1
Mặc dù nó chỉ là một bộ nhớ ảo, nhưng chính xác thì nó giới hạn cách tiếp cận này và do đó nó sẽ không hoạt động đối với các tệp lớn. Tôi đã thử nó với tập tin ~ 1,2 Gb với hơn 10 triệu. các dòng (như thu được với wc -l) và vừa có WindowsError: [Lỗi 8] Không đủ bộ nhớ để xử lý lệnh này. Tất nhiên, đây là một trường hợp cạnh.
SilentGhost

6
+1 cho dữ liệu thời gian thực. Chúng ta có biết kích thước bộ đệm của 1024 * 1024 là tối ưu hay có cái nào tốt hơn không?
Kiv

28
Dường như wccount()là nhanh nhất gist.github.com/0ac760859e614cd03652
JFS

133

Tôi đã phải đăng bài này lên một câu hỏi tương tự cho đến khi điểm danh tiếng của tôi tăng lên một chút (nhờ bất cứ ai làm tôi bực mình!).

Tất cả các giải pháp này đều bỏ qua một cách để làm cho việc này chạy nhanh hơn đáng kể, cụ thể là bằng cách sử dụng giao diện không có bộ đệm (thô), sử dụng phụ, và thực hiện bộ đệm của riêng bạn. (Điều này chỉ áp dụng trong Python 3. Trong Python 2, giao diện thô có thể được sử dụng theo mặc định, nhưng trong Python 3, bạn sẽ mặc định thành Unicode.)

Sử dụng một phiên bản sửa đổi của công cụ thời gian, tôi tin rằng đoạn mã sau nhanh hơn (và hơi nhiều pythonic) hơn bất kỳ giải pháp nào được cung cấp:

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

Sử dụng một chức năng tạo riêng biệt, điều này sẽ chạy nhanh hơn:

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 )

Điều này có thể được thực hiện hoàn toàn với các biểu thức trình tạo nội tuyến bằng itertools, nhưng nó trông khá kỳ lạ:

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 )

Dưới đây là thời gian của tôi:

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

20
Tôi đang làm việc với các tệp 100Gb + và rawgencounts của bạn là giải pháp khả thi duy nhất tôi thấy cho đến nay. Cảm ơn!
soungalo

1
wccounttrong bảng này cho tiến trình con shell wccông cụ?
Anentropic

1
thấy điều này trong bình luận khác, tôi đoán nó là sau đó gist.github.com/zed/0ac760859e614cd03652
Anentropic

3
Cảm ơn @ michael-bacon, đó là một giải pháp thực sự tốt đẹp. Bạn có thể làm cho rawincountgiải pháp bớt lạ hơn bằng cách sử dụng bufgen = iter(partial(f.raw.read, 1024*1024), b'')thay vì kết hợp takewhilerepeat.
Peter H.

1
Ồ, một phần chức năng, yeah, đó là một tinh chỉnh nhỏ tốt đẹp. Ngoài ra, tôi giả định rằng 1024 * 1024 sẽ được trình thông dịch hợp nhất và được coi là một hằng số nhưng đó không phải là tài liệu.
Michael Bacon

90

Bạn có thể thực thi một quy trình con và chạy 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])

6
phiên bản windows này sẽ là gì?
SilentGhost

1
Bạn có thể tham khảo câu hỏi SO này liên quan đến điều đó. stackoverflow.com/questions/247234/
hy

7
Thật vậy, trong trường hợp của tôi (Mac OS X), việc này mất 0,13 giây so với 0,5 giây để đếm số lượng dòng "cho x trong tệp (...)", so với 1,0 giây đếm các cuộc gọi lặp lại cho str.find hoặc mmap.find . (Tệp tôi đã sử dụng để kiểm tra tệp này có 1,3 triệu dòng.)
bentin

1
Không cần liên quan đến vỏ trên đó. chỉnh sửa câu trả lời và thêm mã ví dụ;
nosklo

2
Không phải là nền tảng chéo.
điện tử

42

Đây là một chương trình python để sử dụng thư viện đa xử lý để phân phối dòng đếm trên các máy / lõi. Thử nghiệm của tôi cải thiện việc đếm tệp dòng 20 triệu từ 26 giây đến 7 giây bằng máy chủ windows 64 lõi 8. Lưu ý: không sử dụng ánh xạ bộ nhớ làm cho mọi thứ chậm hơn nhiều.

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

Làm thế nào để điều này làm việc với các tập tin lớn hơn nhiều so với bộ nhớ chính? ví dụ: tệp 20 GB trên hệ thống có RAM 4GB và 2 lõi
Brian Minton

Khó kiểm tra ngay bây giờ, nhưng tôi cho rằng nó sẽ trang tệp vào và ra.
Martlark

5
Đây là mã khá gọn gàng. Tôi đã ngạc nhiên khi thấy rằng nó nhanh hơn để sử dụng nhiều bộ xử lý. Tôi đoán rằng IO sẽ là nút cổ chai. Trong các phiên bản Python cũ hơn, dòng 21 cần int () như chunk = int ((fSize / process)) + 1
Karl Henselin 30/12/14

làm nó tải tất cả các tập tin vào bộ nhớ? Thế còn một đám cháy lớn hơn khi kích thước lớn hơn ram trên máy tính thì sao?
pelos

Các tệp được ánh xạ vào bộ nhớ ảo, vì vậy kích thước của tệp và dung lượng bộ nhớ thực thường không phải là một hạn chế.
Martlark

17

Một giải pháp bash một dòng tương tự như câu trả lời này , sử dụng subprocess.check_outputchức năng hiện đại :

def line_count(filename):
    return int(subprocess.check_output(['wc', '-l', filename]).split()[0])

Câu trả lời này phải được bình chọn lên đến một vị trí cao hơn trong chủ đề này cho người dùng Linux / Unix. Mặc dù có nhiều ưu tiên trong một giải pháp đa nền tảng, đây là một cách tuyệt vời trên Linux / Unix. Đối với tệp csv 184 triệu dòng tôi phải lấy mẫu dữ liệu từ đó, nó cung cấp thời gian chạy tốt nhất. Các giải pháp python thuần khác mất trung bình hơn 100 giây trong khi cuộc gọi của quy trình con wc -lmất ~ 5 giây.
Shan Dou

shell=Truelà xấu cho an ninh, tốt hơn là tránh nó.
Alexey Vazhnov

Điểm công bằng, được chỉnh sửa
1 ''

15

Tôi sẽ sử dụng phương thức đối tượng tệp của Python readlines, như sau:

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

Thao tác này sẽ mở tệp, tạo danh sách các dòng trong tệp, đếm độ dài của danh sách, lưu tệp đó vào một biến và đóng tệp lại.


6
Mặc dù đây là một trong những cách đầu tiên xuất hiện trong đầu, nhưng có lẽ nó không hiệu quả về bộ nhớ, đặc biệt là nếu đếm các dòng trong tệp lên tới 10 GB (như tôi làm), đó là một bất lợi đáng chú ý.
Steen Schütt

@TimeSheep Đây có phải là sự cố đối với các tệp có nhiều dòng (ví dụ: hàng tỷ) dòng nhỏ hoặc tệp có dòng cực dài (giả sử Gigabyte trên mỗi dòng) không?
robert

Lý do tôi hỏi là, dường như trình biên dịch sẽ có thể tối ưu hóa điều này bằng cách không tạo ra một danh sách trung gian.
robert

@dmityugov Mỗi tài liệu Python, xreadlinesđã bị từ chối kể từ 2.3, vì nó chỉ trả về một trình vòng lặp. for line in filelà sự thay thế đã nêu. Xem: docs.python.org/2/l Library / stdtypes.html
Kumba

12
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

12

Đây là những gì tôi sử dụng, có vẻ khá sạch sẽ:

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

CẬP NHẬT: Điều này nhanh hơn một chút so với sử dụng python thuần nhưng với chi phí sử dụng bộ nhớ. Quá trình con sẽ phân nhánh một tiến trình mới có cùng dấu chân bộ nhớ với tiến trình cha trong khi nó thực thi lệnh của bạn.


1
Như một lưu ý phụ, điều này sẽ không hoạt động trên Windows.
Bram Vanroy

utils core dường như cung cấp "wc" cho windows stackoverflow.com/questions/247234/ . Bạn cũng có thể sử dụng máy ảo linux trong hộp windows nếu mã của bạn sẽ chạy trong linux trong prod.
radtek

Hoặc WSL, rất khuyến khích mọi VM nếu những thứ như thế này là điều duy nhất bạn làm. :-)
Bram Vanroy

Vâng, nó hoạt động. Tôi không phải là người chơi windows nhưng từ việc goolging tôi đã học được WSL = Hệ thống con Windows cho Linux =)
radtek

3
python3.7: byte trả về quy trình con, vì vậy mã trông như thế này: int (sub process.checkDefput (['wc', '-l', file_path]). decode ("utf-8"). lstrip (). split (" ") [0])
Alexey Alexeenka

11

Đây là điều nhanh nhất tôi đã tìm thấy bằng cách sử dụng trăn nguyên chất. Bạn có thể sử dụng bất kỳ dung lượng bộ nhớ nào bạn muốn bằng cách đặt bộ đệm, mặc dù 2 ** 16 dường như là một điểm ngọt trên máy tính của tôi.

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), ''))

Tôi đã tìm thấy câu trả lời ở đây Tại sao việc đọc các dòng từ stdin chậm hơn nhiều so với Python? và điều chỉnh nó chỉ là một chút nhỏ. Đây là một bài đọc rất tốt để hiểu cách đếm các dòng một cách nhanh chóng, mặc dù vậy wc -lvẫn nhanh hơn khoảng 75% so với bất kỳ thứ gì khác.


9

Tôi đã có một cải tiến nhỏ (4-8%) với phiên bản này sử dụng lại bộ đệm liên tục để nó tránh mọi bộ nhớ hoặc chi phí hoạt động của GC:

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

Bạn có thể chơi xung quanh với kích thước bộ đệm và có thể thấy một chút cải thiện.


Đẹp. Để tính đến các tệp không kết thúc bằng \ n, hãy thêm 1 bên ngoài vòng lặp nếu bộ đệm và bộ đệm [-1]! = '\ N'
ryuusenshi

Một lỗi: bộ đệm trong vòng cuối cùng có thể không sạch.
Jay

Điều gì xảy ra nếu ở giữa các bộ đệm, một phần kết thúc bằng \ và phần còn lại bắt đầu bằng n? điều đó sẽ bỏ lỡ một dòng mới trong đó, tôi rất muốn các biến để lưu trữ phần cuối và phần đầu của mỗi đoạn, nhưng điều đó có thể thêm thời gian vào tập lệnh = (
pelos

9

Câu trả lời của Kyle

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

có lẽ là tốt nhất, một sự thay thế cho điều này là

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

Dưới đây là so sánh hiệu suất của cả hai

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

9

Giải pháp một dòng:

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

Đoạn trích của tôi:

>>> os.system('wc -l *.txt')

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

Ý kiến ​​hay, thật không may, điều này không hoạt động trên Windows.
Kim

3
Nếu bạn muốn trở thành người lướt sóng của trăn, hãy nói lời tạm biệt với windows. Hãy tin tôi một ngày nào đó bạn sẽ cảm ơn tôi.
TheExorcist

6
Tôi chỉ xem xét một điều đáng chú ý là nó sẽ chỉ hoạt động trên windows. Tôi thích tự mình làm việc trên một ngăn xếp linux / unix, nhưng khi viết phần mềm IMHO, người ta nên xem xét các tác dụng phụ mà một chương trình có thể có khi chạy trong các hệ điều hành khác nhau. Vì OP không đề cập đến nền tảng của anh ấy và trong trường hợp có ai đó bật lên giải pháp này thông qua google và sao chép nó (không biết về những hạn chế mà hệ thống Windows có thể có), tôi muốn thêm ghi chú.
Kim

Bạn không thể lưu đầu ra của os.system()biến và xử lý hậu kỳ bằng mọi cách.
An Se

@AnSe bạn đúng nhưng câu hỏi không được hỏi liệu nó có tiết kiệm hay không. Tôi đoán bạn đang hiểu ngữ cảnh.
TheExorcist

6

Chỉ để hoàn thành các phương pháp trên, tôi đã thử một biến thể với mô-đun fileinput:

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

Và đã chuyển một tệp 60 triệu cho tất cả các phương pháp đã nêu ở trên:

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

Đối với tôi, điều hơi ngạc nhiên là fileinput rất tệ và quy mô tệ hơn nhiều so với tất cả các phương pháp khác ...


5

Đối với tôi, biến thể này sẽ là nhanh nhất:

#!/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()

Lý do: đệm nhanh hơn đọc từng dòng và string.countcũng rất nhanh


1
Nhưng nó là? Ít nhất là trên OSX / python2.5, phiên bản của OP vẫn nhanh hơn khoảng 10% theo timeit.py.
dF.

Nếu dòng cuối cùng không kết thúc bằng '\ n' thì sao?
tzot

1
Tôi không biết bạn đã thử nó như thế nào, dF, nhưng trên máy của tôi, nó chậm hơn 2,5 lần so với bất kỳ tùy chọn nào khác.
SilentGhost

34
Bạn nói rằng nó sẽ là nhanh nhất và sau đó nói rằng bạn đã không kiểm tra nó. Không khoa học lắm nhỉ? :)
Ólafur Chờ đợi

Xem giải pháp và số liệu thống kê được cung cấp bởi Ryan Ginstrom trả lời dưới đây. Ngoài ra hãy xem nhận xét và liên kết của JF Sebastian trên cùng một câu trả lời.
SherylHohman

5

Mã này ngắn hơn và rõ ràng hơn. Đây có lẽ là cách tốt nhất:

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

6
Bạn cũng nên đóng tập tin.
rsm

6
Nó sẽ tải toàn bộ tập tin vào bộ nhớ.
Ivelin

không tốt nhất khi cần hiệu suất trên các tệp lớn
mabraham

4

Tôi đã sửa đổi trường hợp bộ đệm như thế này:

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

Bây giờ cũng có các tệp trống và dòng cuối cùng (không có \ n) được tính.


Cũng có thể giải thích (hoặc thêm nhận xét trong mã) những gì bạn đã thay đổi và để làm gì;). Có thể cung cấp cho mọi người nhiều hơn bên trong mã của bạn dễ dàng hơn nhiều (thay vì "phân tích" mã trong não).
Styxxy

Tối ưu hóa vòng lặp tôi nghĩ cho phép Python thực hiện tra cứu biến cục bộ tại read_f, python.org/doc/essays/list2str
The Red Pea

3

Cái này thì sao

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



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

3

Nếu ai đó muốn có được số lượng dòng giá rẻ trong Python trong Linux, tôi khuyên bạn nên sử dụng phương pháp này:

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

file_path có thể là cả đường dẫn tệp trừu tượng hoặc đường dẫn tương đối. Hy vọng điều này có thể giúp đỡ.


2

Còn cái này thì sao?

import fileinput
import sys

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

fileinput.close()
print counter

2

Làm thế nào về điều này một lót:

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

Mất 0,003 giây bằng cách sử dụng phương pháp này để hẹn giờ trên tệp dòng 3900

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

2
def count_text_file_lines(path):
    with open(path, 'rt') as file:
        line_count = sum(1 for _line in file)
    return line_count

Bạn có thể vui lòng giải thích những gì sai với nó nếu bạn nghĩ rằng nó là sai? Nó làm việc cho tôi. Cảm ơn!
jciloa

Tôi sẽ quan tâm tại sao câu trả lời này cũng bị hạ thấp. Nó lặp lại các tập tin theo dòng và tổng hợp chúng. Tôi thích nó, nó ngắn và đến mức, có gì sai với nó?
giám khảo

2

Phương pháp đơn giản:

1)

>>> f = len(open("myfile.txt").readlines())
>>> f

430

2)

>>> f = open("myfile.txt").read().count('\n')
>>> f
430
>>>

3)

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

3
Trong ví dụ này tập tin không được đóng.
Maciej M

9
OP muốn một cái gì đó hiệu quả bộ nhớ. Điều này chắc chắn không phải là nó.
Andy Carlson

1

kết quả của việc mở tệp là một trình vòng lặp, có thể được chuyển đổi thành một chuỗi, có độ dài:

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

điều này ngắn gọn hơn vòng lặp rõ ràng của bạn và tránh enumerate.


10
có nghĩa là tệp 100 Mb sẽ cần phải được đọc vào bộ nhớ.
SilentGhost

vâng, điểm tốt, mặc dù tôi tự hỏi về sự khác biệt tốc độ (trái ngược với bộ nhớ). Có thể tạo ra một trình vòng lặp thực hiện điều này, nhưng tôi nghĩ nó sẽ tương đương với giải pháp của bạn.
Andrew Jaffe

6
-1, nó không chỉ là bộ nhớ mà còn phải xây dựng danh sách trong bộ nhớ.
orip

0

Bạn có thể sử dụng os.pathmô-đun theo cách sau:

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

, đâu Filenamelà đường dẫn tuyệt đối của tập tin.


1
Câu trả lời này có liên quan os.pathgì?
moi

0

Nếu tập tin có thể vừa với bộ nhớ, thì

with open(fname) as f:
    count = len(f.read().split(b'\n')) - 1
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.