Bagaimana cara mendapatkan jumlah baris file besar dengan murah di Python?

1012

Saya perlu mendapatkan jumlah baris file besar (ratusan ribu baris) dengan python. Apa cara paling efisien baik dari segi memori maupun waktu?

Saat ini saya lakukan:

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

Apakah mungkin melakukan yang lebih baik?

SilentGhost
sumber
7
Apakah Anda memerlukan jumlah baris yang tepat atau cukupkah perkiraan?
pico
43
Saya akan menambahkan i = -1 sebelum untuk loop, karena kode ini tidak berfungsi untuk file kosong.
Maciek Sawicki
12
@ Legenda: Saya yakin pico berpikir, dapatkan ukuran file (dengan seek (0,2) atau equiv), bagi dengan panjang garis perkiraan. Anda bisa membaca beberapa baris di awal untuk menebak panjang garis rata-rata.
Anne
32
enumerate(f, 1)dan parit i + 1?
Ian Mackinnon
4
@IanMackinnon Berfungsi untuk file kosong, tetapi Anda harus menginisialisasi i ke 0 sebelum for-loop.
scai

Jawaban:

357

Anda tidak bisa mendapatkan yang lebih baik dari itu.

Bagaimanapun, solusi apa pun harus membaca seluruh file, mencari tahu berapa banyak yang \nAnda miliki, dan mengembalikan hasilnya.

Apakah Anda memiliki cara yang lebih baik untuk melakukannya tanpa membaca seluruh file? Tidak yakin ... Solusi terbaik akan selalu I / O-terikat, terbaik yang dapat Anda lakukan adalah memastikan Anda tidak menggunakan memori yang tidak perlu, tetapi sepertinya Anda memiliki yang tertutup.

Yuval Adam
sumber
7
Tepat, bahkan WC membaca file, tetapi dalam C dan itu mungkin cukup dioptimalkan.
Ólafur Waage
6
Sejauh yang saya mengerti file Python IO dilakukan melalui C juga. docs.python.org/library/stdtypes.html#file-objects
Tomalak
9
@ Tomalak Itu herring merah. Sementara python dan wc mungkin mengeluarkan syscalls yang sama, python memiliki overhead pengiriman opcode yang tidak dimiliki wc.
bobpoekert
4
Anda dapat memperkirakan jumlah garis dengan pengambilan sampel. Ini bisa ribuan kali lebih cepat. Lihat: documentroot.com/2011/02/…
Erik Aronesty
4
Jawaban lain tampaknya menunjukkan bahwa jawaban kategoris ini salah, dan karenanya harus dihapus daripada disimpan sebagai diterima.
Skippy le Grand Gourou
625

Satu baris, mungkin cukup cepat:

num_lines = sum(1 for line in open('myfile.txt'))
Kyle
sumber
8
mirip dengan jumlah (urutan 1) setiap baris dihitung sebagai 1. >>> [1 untuk garis dalam rentang (10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] >>> jumlah (1 untuk garis dalam kisaran (10)) 10 >>>
James Sapam
4
num_lines = jumlah (1 untuk baris terbuka ('myfile.txt') jika line.rstrip ()) untuk filter baris kosong
Honghe.Wu
61
saat kita membuka file, apakah ini akan ditutup secara otomatis setelah kita beralih ke semua elemen? Apakah harus 'tutup ()'? Saya pikir kita tidak bisa menggunakan 'dengan open ()' dalam pernyataan singkat ini, kan?
Mannaggia
16
@Mannaggia Anda benar, akan lebih baik menggunakan 'dengan terbuka (nama file)' untuk memastikan file ditutup ketika selesai, dan bahkan lebih baik melakukan ini dalam blok coba-kecuali, di mana pengecualian dan IOError dilemparkan jika file tidak dapat dibuka.
BoltzmannBrain
17
Hal lain yang perlu diperhatikan: Ini ~ 0,04-0,05 detik lebih lambat daripada yang diberikan masalah asli pada file teks 300 ribu baris
andrew
202

Saya percaya bahwa file yang dipetakan memori akan menjadi solusi tercepat. Saya mencoba empat fungsi: fungsi yang diposting oleh OP ( opcount); iterasi sederhana di atas baris dalam file ( simplecount); readline dengan memori yang dipetakan diajukan (mmap) ( mapcount); dan solusi baca buffer yang ditawarkan oleh Mykola Kharechko ( bufcount).

Saya menjalankan setiap fungsi lima kali, dan menghitung rata-rata run-time untuk file teks 1,2 juta-baris.

Windows XP, Python 2.5, RAM 2GB, prosesor AMD 2 GHz

Inilah hasil saya:

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

Sunting : angka untuk Python 2.6:

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

Jadi strategi membaca buffer tampaknya menjadi yang tercepat untuk Windows / Python 2.6

Ini kodenya:

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))
Ryan Ginstrom
sumber
1
Seluruh file yang dipetakan dengan memori tidak dimuat ke dalam memori. Anda mendapatkan ruang memori virtual, tempat OS masuk dan keluar dari RAM sesuai kebutuhan. Inilah cara mereka ditangani pada Windows: msdn.microsoft.com/en-us/library/ms810613.aspx
Ryan Ginstrom
1
Maaf, berikut ini adalah referensi yang lebih umum tentang file yang dipetakan dengan memori: en.wikipedia.org/wiki/Memory-mapped_file Dan terima kasih atas suaranya. :)
Ryan Ginstrom
1
Meskipun itu hanya memori virtual, justru inilah yang membatasi pendekatan ini dan karena itu tidak akan berfungsi untuk file besar. Saya sudah mencobanya dengan ~ 1,2 Gb file dengan lebih dari 10 juta. baris (seperti yang diperoleh dengan wc -l) dan baru saja mendapat WindowsError: [Kesalahan 8] Tidak cukup penyimpanan tersedia untuk memproses perintah ini. tentu saja, ini adalah kasus tepi.
SilentGhost
6
+1 untuk data waktu nyata. Apakah kita tahu jika ukuran buffer 1024 * 1024 optimal, atau ada yang lebih baik?
Kiv
28
Tampaknya itu wccount()adalah gist.github.com/0ac760859e614cd03652
jfs
133

Saya harus memposting ini pada pertanyaan yang sama sampai skor reputasi saya melonjak sedikit (terima kasih kepada siapa pun yang menabrak saya!).

Semua solusi ini mengabaikan satu cara untuk membuat ini berjalan jauh lebih cepat, yaitu dengan menggunakan antarmuka (mentah) unbuffered, menggunakan bytearrays, dan melakukan buffering Anda sendiri. (Ini hanya berlaku dalam Python 3. Dalam Python 2, antarmuka mentah mungkin atau mungkin tidak digunakan secara default, tetapi dalam Python 3, Anda akan default ke Unicode.)

Menggunakan versi modifikasi dari alat penghitung waktu, saya percaya kode berikut ini lebih cepat (dan sedikit lebih pythonic) daripada salah satu solusi yang ditawarkan:

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

Menggunakan fungsi generator terpisah, ini menjalankan smidge lebih cepat:

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 )

Ini dapat dilakukan sepenuhnya dengan ekspresi generator secara in-line menggunakan itertools, tetapi terlihat sangat aneh:

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 )

Inilah waktu saya:

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
Michael Bacon
sumber
20
Saya bekerja dengan 100Gb + file, dan rawgencount Anda adalah satu-satunya solusi yang layak saya lihat sejauh ini. Terima kasih!
soungalo
1
ada wccountdi tabel ini untuk wcalat shell subprocess ?
Anentropic
1
menemukan ini di komentar lain, saya kira itu kemudian gist.github.com/zed/0ac760859e614cd03652
Anentropic
3
Terima kasih @ michael-bacon, ini solusi yang sangat bagus. Anda dapat membuat rawincountsolusi terlihat kurang aneh dengan menggunakan bufgen = iter(partial(f.raw.read, 1024*1024), b'')alih-alih menggabungkan takewhiledan repeat.
Peter H.
1
Oh, fungsi parsial, ya, itu sedikit perubahan. Juga, saya berasumsi bahwa 1024 * 1024 akan digabungkan oleh penerjemah dan diperlakukan sebagai konstanta tetapi itu pada firasat bukan dokumentasi.
Michael Bacon
90

Anda dapat menjalankan subproses dan menjalankannya 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])
Ólafur Waage
sumber
6
apa yang akan menjadi versi windows ini?
SilentGhost
1
Anda dapat merujuk pada pertanyaan SO ini mengenai hal itu. stackoverflow.com/questions/247234/…
Ólafur Waage
7
Memang, dalam kasus saya (Mac OS X) ini membutuhkan 0,13 vs 0,5 untuk menghitung jumlah baris "untuk x dalam file (...)" menghasilkan, versus 1,0 menghitung panggilan berulang ke str.find atau mmap.find . (File yang saya gunakan untuk menguji ini memiliki 1,3 juta baris.)
bendin
1
Tidak perlu melibatkan shell pada itu. jawaban yang diedit dan menambahkan kode contoh;
nosklo
2
Bukan lintas platform.
e-info128
42

Berikut ini adalah program python untuk menggunakan pustaka multiprocessing untuk mendistribusikan penghitungan baris di seluruh mesin / inti. Pengujian saya meningkatkan penghitungan file baris 20 juta dari 26 detik hingga 7 detik menggunakan server 8 core windows 64. Catatan: tidak menggunakan pemetaan memori membuat segalanya lebih lambat.

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 ) )
Martlark
sumber
Bagaimana cara kerjanya dengan file yang jauh lebih besar dari memori utama? misalnya file 20GB pada sistem dengan 4GB RAM dan 2 core
Brian Minton
Sulit untuk menguji sekarang, tapi saya kira itu akan membuka halaman file masuk dan keluar.
Martlark
5
Ini kode yang cukup rapi. Saya terkejut menemukan bahwa lebih cepat menggunakan banyak prosesor. Saya pikir IO akan menjadi hambatan. Dalam versi Python yang lebih lama, baris 21 membutuhkan int () seperti chunk = int ((fSize / proses)) + 1
Karl Henselin
apakah ini memuat semua file ke dalam memori? bagaimana dengan api yang lebih besar di mana ukurannya lebih besar dari ram di komputer?
pelos
File-file tersebut dipetakan ke dalam memori virtual, sehingga ukuran file dan jumlah memori aktual biasanya bukan batasan.
Martlark
17

Solusi bash satu baris yang mirip dengan jawaban ini , menggunakan subprocess.check_outputfungsi modern :

def line_count(filename):
    return int(subprocess.check_output(['wc', '-l', filename]).split()[0])
1 ''
sumber
Jawaban ini harus dipilih ke tempat yang lebih tinggi di utas ini untuk pengguna Linux / Unix. Terlepas dari preferensi mayoritas dalam solusi lintas-platform, ini adalah cara yang luar biasa di Linux / Unix. Untuk file csv 184 juta baris yang saya harus sampel data, itu memberikan runtime terbaik. Solusi python murni lainnya memakan waktu rata-rata 100+ detik sedangkan panggilan subproses wc -lmembutuhkan waktu ~ 5 detik.
Shan Dou
shell=Trueburuk untuk keamanan, lebih baik menghindarinya.
Alexey Vazhnov
Poin wajar, diedit
1 ''
15

Saya akan menggunakan metode objek file Python readlines, sebagai berikut:

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

Ini membuka file, membuat daftar baris dalam file, menghitung panjang daftar, menyimpannya ke variabel dan menutup file lagi.

Daniel Lee
sumber
6
Meskipun ini adalah salah satu cara pertama yang terlintas dalam pikiran, itu mungkin tidak terlalu efisien dalam memori, terutama jika menghitung baris dalam file hingga 10 GB (Seperti yang saya lakukan), yang merupakan kerugian yang patut dicatat.
Steen Schütt
@ TimeSheep Apakah ini masalah untuk file dengan banyak (katakanlah, miliaran) baris kecil, atau file yang memiliki garis sangat panjang (katakanlah, Gigabytes per baris)?
robert
Alasan saya bertanya adalah, tampaknya kompiler harus dapat mengoptimalkan ini dengan tidak membuat daftar perantara.
robert
@dmityugov Per Python docs, xreadlinestelah ditinggalkan sejak 2.3, karena hanya mengembalikan iterator. for line in fileadalah pengganti yang disebutkan. Lihat: docs.python.org/2/library/stdtypes.html#file.xreadlines
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
pkit
sumber
12

Inilah yang saya gunakan, sepertinya cukup bersih:

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

UPDATE: Ini sedikit lebih cepat daripada menggunakan python murni tetapi dengan biaya penggunaan memori. Subprocess akan melakukan proses baru dengan jejak memori yang sama dengan proses induk saat menjalankan perintah Anda.

radtek
sumber
1
Sama seperti catatan tambahan, ini tidak akan bekerja pada Windows tentu saja.
Bram Vanroy
core utils rupanya menyediakan "wc" untuk windows stackoverflow.com/questions/247234/… . Anda juga dapat menggunakan linux VM di kotak windows Anda jika kode Anda akan berakhir berjalan di linux di prod.
radtek
Atau WSL, sangat disarankan untuk VM jika hal-hal seperti ini adalah satu-satunya yang Anda lakukan. :-)
Bram Vanroy
Ya itu berhasil. Saya bukan orang windows tetapi dari goolging saya belajar WSL = Windows Subsystem untuk Linux =)
radtek
3
python3.7: subprocess return bytes, sehingga kode terlihat seperti ini: int (subprocess.check_output (['wc', '-l', file_path]). decode ("utf-8"). lstrip (). split (" ") [0])
Alexey Alexeenka
11

Ini adalah hal tercepat yang saya temukan menggunakan python murni. Anda dapat menggunakan jumlah memori berapa pun yang Anda inginkan dengan mengatur buffer, meskipun 2 ** 16 tampaknya menjadi sweet spot di komputer saya.

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

Saya menemukan jawabannya di sini. Mengapa membaca baris dari stdin jauh lebih lambat di C ++ daripada Python? dan men-tweak sedikit saja. Ini adalah bacaan yang sangat baik untuk memahami cara menghitung garis dengan cepat, meskipun wc -lmasih sekitar 75% lebih cepat dari yang lainnya.

jeffpkamp
sumber
9

Saya mendapat peningkatan kecil (4-8%) dengan versi ini yang menggunakan kembali buffer konstan sehingga harus menghindari memori atau overhead GC:

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

Anda dapat bermain-main dengan ukuran buffer dan mungkin melihat sedikit peningkatan.

Scott Persinger
sumber
Bagus. Untuk memperhitungkan file yang tidak diakhiri dengan \ n, tambahkan 1 di luar loop jika buffer dan buffer [-1]! = '\ N'
ryuusenshi
Bug: buffer di babak terakhir mungkin tidak bersih.
Jay
bagaimana jika di antara buffer satu bagian diakhiri dengan \ dan bagian lainnya dimulai dengan n? yang akan melewatkan satu baris baru di sana, saya akan sudgest ke variabel untuk menyimpan akhir dan awal setiap potongan, tapi itu mungkin menambah lebih banyak waktu untuk script = (
pelos
9

Jawaban Kyle

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

mungkin yang terbaik, alternatif untuk ini

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

Berikut ini perbandingan kinerja keduanya

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
ChillarAnand
sumber
9

Solusi satu baris:

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

Cuplikan saya:

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

0 bar.txt
1000 command.txt
3 test_file.txt
1003 total
Pengusir setan
sumber
Ide bagus, sayangnya ini tidak bekerja di Windows sekalipun.
Kim
3
jika Anda ingin menjadi peselancar python, ucapkan selamat tinggal pada windows. Percayalah, suatu hari Anda akan berterima kasih kepada saya.
TheExorcist
6
Saya hanya menganggap penting bahwa ini hanya akan bekerja di windows. Saya lebih suka bekerja di linux / unix stack sendiri, tetapi ketika menulis perangkat lunak IMHO orang harus mempertimbangkan efek samping yang bisa dimiliki sebuah program ketika dijalankan di bawah OS yang berbeda. Karena OP tidak menyebutkan platformnya dan kalau-kalau ada yang muncul pada solusi ini melalui google dan menyalinnya (tidak mengetahui keterbatasan yang mungkin dimiliki sistem Windows), saya ingin menambahkan catatan.
Kim
Anda tidak dapat menyimpan output os.system()ke variabel dan mempostingnya lagi.
An Se
@Anda Anda benar tetapi pertanyaan tidak ditanyakan apakah menyimpan atau tidak. Saya kira Anda memahami konteksnya.
TheExorcist
6

Hanya untuk melengkapi metode di atas saya mencoba varian dengan modul fileinput:

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

Dan melewati file 60mil baris ke semua metode yang disebutkan di atas:

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

Ini sedikit mengejutkan bagi saya bahwa fileinput adalah yang buruk dan skala jauh lebih buruk daripada semua metode lain ...

BandGap
sumber
5

Bagi saya varian ini akan menjadi yang tercepat:

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

alasan: buffering lebih cepat daripada membaca baris demi baris dan string.countjuga sangat cepat

Mykola Kharechko
sumber
1
Tetapi apakah itu? Setidaknya pada OSX / python2.5 versi OP masih sekitar 10% lebih cepat menurut timeit.py.
dF.
Bagaimana jika baris terakhir tidak diakhiri dengan '\ n'?
tzot
1
Saya tidak tahu bagaimana Anda mengujinya, dF, tetapi pada mesin saya ~ 2,5 kali lebih lambat dari opsi lainnya.
SilentGhost
34
Anda menyatakan bahwa itu akan menjadi yang tercepat dan kemudian menyatakan bahwa Anda belum mengujinya. Tidak terlalu ilmiah ya? :)
Ólafur Waage
Lihat solusi dan statistik yang disediakan oleh Ryan Ginstrom jawab di bawah. Lihat juga komentar JF Sebastian dan tautan pada jawaban yang sama.
SherylHohman
5

Kode ini lebih pendek dan lebih jelas. Itu mungkin cara terbaik:

num_lines = open('yourfile.ext').read().count('\n')
Texom512
sumber
6
Anda juga harus menutup file.
rsm
6
Ini akan memuat seluruh file ke dalam memori.
Ivelin
tidak terbaik ketika membutuhkan kinerja pada file besar
mabraham
4

Saya telah memodifikasi kasing buffer seperti ini:

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

Sekarang juga kosongkan file dan baris terakhir (tanpa \ n) dihitung.

Dummy
sumber
Mungkin juga menjelaskan (atau menambahkan komentar dalam kode) apa yang Anda ubah dan untuk apa;). Mungkin memberi orang lebih banyak di dalam kode Anda lebih mudah (daripada "parsing" kode di otak).
Styxxy
Optimalisasi pengulangan menurut saya memungkinkan Python untuk melakukan pencarian variabel lokal di read_f, python.org/doc/essays/list2str
The Red Pea
3

Bagaimana dengan ini

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

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

pyanon
sumber
Ini memberikan hitung -1 dari nilai sebenarnya.
Borealis
Argumen kedua opsional enumerate()adalah mulai menghitung menurut docs.python.org/2/library/functions.html#enumerate
MarkHu
3
print open('file.txt', 'r').read().count("\n") + 1
Andrés Torres
sumber
3
def line_count(path):
    count = 0
    with open(path) as lines:
        for count, l in enumerate(lines, start=1):
            pass
    return count
mdwhatcott
sumber
3

Jika seseorang ingin mendapatkan jumlah baris murah di Python di Linux, saya merekomendasikan metode ini:

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

file_path dapat berupa path file abstrak atau path relatif. Semoga ini bisa membantu.

Lerner Zhang
sumber
2

Bagaimana dengan ini?

import fileinput
import sys

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

fileinput.close()
print counter
leba-lev
sumber
2

Bagaimana dengan one-liner ini:

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

Membutuhkan 0,003 detik menggunakan metode ini untuk menghitung waktu pada file baris 3900

def c():
  import time
  s = time.time()
  file_length = len(open('myfile.txt','r').read().split('\n'))
  print time.time() - s
onetwopunch
sumber
2
def count_text_file_lines(path):
    with open(path, 'rt') as file:
        line_count = sum(1 for _line in file)
    return line_count
jciloa
sumber
Bisakah Anda jelaskan apa yang salah dengan itu jika Anda pikir itu salah? Ini berhasil untuk saya. Terima kasih!
jciloa
Saya akan tertarik pada mengapa jawaban ini juga diturunkan. Itu beralih di atas file dengan baris dan meringkasnya. Saya suka, pendek dan to the point, apa yang salah dengan itu?
penilai
2

Metode sederhana:

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')))
Mohideen bin Mohammed
sumber
3
Dalam contoh ini file tidak ditutup.
Maciej M
9
OP menginginkan sesuatu yang efisien memori. Ini jelas bukan.
Andy Carlson
1

hasil dari pembukaan file adalah iterator, yang dapat dikonversi ke urutan, yang memiliki panjang:

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

ini lebih ringkas daripada loop eksplisit Anda, dan menghindari enumerate.

Andrew Jaffe
sumber
10
yang berarti bahwa file 100 Mb perlu dibaca ke dalam memori.
SilentGhost
ya, poin bagus, meskipun saya bertanya-tanya tentang perbedaan kecepatan (bukan memori). Mungkin memungkinkan untuk membuat iterator yang melakukan ini, tapi saya pikir itu akan setara dengan solusi Anda.
Andrew Jaffe
6
-1, ini bukan hanya memori, tetapi harus membuat daftar dalam memori.
orip
0

Anda dapat menggunakan os.pathmodul dengan cara berikut:

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

, di mana Filenamejalur absolut file.

Pemenang
sumber
1
Apa hubungannya jawaban ini os.path?
moi
0

Jika file tersebut dapat masuk ke dalam memori, maka

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