Jak tanio uzyskać liczbę wierszy dużego pliku w Pythonie?

1010

Potrzebuję uzyskać liczbę wierszy dużego pliku (setki tysięcy linii) w pythonie. Jaki jest najbardziej efektywny sposób pod względem pamięci i czasu?

W tej chwili robię:

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

czy można zrobić coś lepszego?

SilentGhost
źródło
7
Czy potrzebujesz dokładnej liczby wierszy, czy wystarczy przybliżenie?
pico
43
Dodałbym wcześniej i = -1 dla pętli, ponieważ ten kod nie działa dla pustych plików.
Maciek Sawicki
12
@ Legend: Założę się, że pico myśli, uzyskaj rozmiar pliku (z seek (0,2) lub equiv), podziel przez przybliżoną długość linii. Na początku możesz przeczytać kilka linii, aby odgadnąć średnią długość linii.
Anne
32
enumerate(f, 1)i porzucić i + 1?
Ian Mackinnon
4
@IanMackinnon Działa dla pustych plików, ale musisz zainicjować i na 0 przed pętlą for.
scai

Odpowiedzi:

356

Nie ma nic lepszego niż to.

W końcu każde rozwiązanie będzie musiało przeczytać cały plik, dowiedzieć się, ile \nmasz i zwrócić ten wynik.

Czy masz lepszy sposób, aby to zrobić bez czytania całego pliku? Nie jestem pewien ... Najlepszym rozwiązaniem zawsze będzie I / O, najlepiej, abyś nie używał niepotrzebnej pamięci, ale wygląda na to, że ją masz.

Yuval Adam
źródło
7
Dokładnie, nawet WC czyta plik, ale w C i jest prawdopodobnie dość zoptymalizowany.
Ólafur Waage,
6
O ile rozumiem plik Python IO odbywa się również przez C. docs.python.org/library/stdtypes.html#file-objects
Tomalak
9
@Tomalak To czerwony śledź. Podczas gdy python i wc mogą wydawać te same wywołania systemowe, python ma narzut wysyłki kodów, których wc nie ma.
bobpoekert
4
Możesz przybliżać liczbę wierszy, próbkując. Może być tysiące razy szybszy. Zobacz: documentroot.com/2011/02/...
Erik Aronesty
4
Inne odpowiedzi wydają się wskazywać, że ta kategoryczna odpowiedź jest błędna, dlatego należy ją usunąć, a nie zachować jako zaakceptowaną.
Skippy le Grand Gourou,
624

Jedna linia, prawdopodobnie dość szybka:

num_lines = sum(1 for line in open('myfile.txt'))
Kyle
źródło
8
jest podobny do sumy (sekwencja 1) każda linia liczy się jako 1. >>> [1 dla linii w zakresie (10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] >>> suma (1 dla linii w zakresie (10)) 10 >>>
James Sapam
4
num_lines = suma (1 dla linii w otwartym ('myfile.txt') if line.rstrip ()) dla filtrowania pustych linii
Honghe.Wu
61
czy podczas otwierania pliku zostanie on automatycznie zamknięty, gdy przejdziemy przez wszystkie elementy? Czy wymagane jest „close ()”? Myślę, że nie możemy używać „z open ()” w tym krótkim zestawieniu, prawda?
Mannaggia
16
@Mannaggia masz rację, lepiej byłoby użyć „z otwartym (nazwa pliku)”, aby upewnić się, że plik zostanie zamknięty po zakończeniu, a jeszcze lepiej robi to w bloku try-wyjątkiem, w którym zgłaszany jest wyjątek i IOError, jeśli pliku nie można otworzyć.
BoltzmannBrain
17
Kolejna rzecz do zapamiętania: jest to ~ 0,04-0,05 sekundy wolniej niż pierwotny problem podany w 300 000 wierszowym pliku tekstowym
andrew
202

Wierzę, że plik odwzorowany w pamięci będzie najszybszym rozwiązaniem. Próbowałem czterech funkcji: funkcja wysłana przez OP ( opcount); prosta iteracja po liniach w pliku ( simplecount); readline z plikiem zmapowanym w pamięci (mmap) ( mapcount); oraz rozwiązanie odczytu bufora oferowane przez Mykola Kharechko ( bufcount).

Uruchomiłem każdą funkcję pięć razy i obliczyłem średni czas działania dla pliku tekstowego o wielkości 1,2 miliona wierszy.

Windows XP, Python 2.5, 2 GB pamięci RAM, procesor AMD 2 GHz

Oto moje wyniki:

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

Edycja : liczby dla Python 2.6:

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

Tak więc strategia odczytu buforów wydaje się być najszybsza dla Windows / Python 2.6

Oto kod:

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
źródło
1
Cały plik odwzorowany w pamięci nie jest ładowany do pamięci. Otrzymujesz wirtualną przestrzeń pamięci, którą system operacyjny zamienia w RAM i z niej w razie potrzeby. Oto jak są obsługiwane w systemie Windows: msdn.microsoft.com/en-us/library/ms810613.aspx
Ryan Ginstrom
1
Przepraszamy, oto bardziej ogólne odniesienie do plików mapowanych w pamięci: en.wikipedia.org/wiki/Memory-mapped_file I dziękuję za głosowanie. :)
Ryan Ginstrom
1
Mimo że jest to tylko pamięć wirtualna, właśnie to ogranicza to podejście i dlatego nie będzie działać w przypadku dużych plików. Wypróbowałem to z plikiem ~ 1,2 Gb z ponad 10 milionami. wiersze (uzyskane za pomocą wc -l) i właśnie dostał błąd Windows: [Błąd 8] Brak wystarczającej ilości pamięci do przetworzenia tego polecenia. oczywiście jest to przypadek skrajny.
SilentGhost
6
+1 za rzeczywiste dane pomiaru czasu. Czy wiemy, czy rozmiar bufora 1024 * 1024 jest optymalny, czy jest lepszy?
Kiv
28
Wydaje się, że wccount()jest to najszybszy gist.github.com/0ac760859e614cd03652
jfs
133

Musiałem napisać to na podobne pytanie, dopóki moja ocena reputacji nieco nie podskoczyła (dzięki temu, kto mnie uderzył!).

Wszystkie te rozwiązania ignorują jeden ze sposobów na przyspieszenie tego działania, a mianowicie użycie niebuforowanego (surowego) interfejsu, używanie bajtów i tworzenie własnego bufora. (Dotyczy to tylko Pythona 3. W Pythonie 2 domyślny interfejs może, ale nie musi być używany, ale w Pythonie 3 domyślnie zostanie ustawiony Unicode.)

Korzystając ze zmodyfikowanej wersji narzędzia do pomiaru czasu, uważam, że następujący kod jest szybszy (i nieznacznie bardziej pythoniczny) niż którekolwiek z oferowanych rozwiązań:

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

Korzystanie z osobnej funkcji generatora przyspiesza smidge:

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 )

Można to zrobić całkowicie za pomocą wyrażeń generatorów wbudowanych za pomocą itertools, ale robi się dość dziwnie:

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 )

Oto moje czasy:

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
źródło
20
Pracuję z plikami 100 Gb +, a twoje rawgencounts to jedyne możliwe do tej pory rozwiązanie. Dzięki!
soungalo,
1
jest wccountw tej tabeli dla wcnarzędzia powłoki podprocesowej ?
Anentropic
1
Znalazłem to w innym komentarzu, myślę, że to gist.github.com/zed/0ac760859e614cd03652
Anentropic
3
Dzięki @ michael-bekon, to naprawdę fajne rozwiązanie. Możesz sprawić, że rawincountrozwiązanie będzie mniej dziwne, używając bufgen = iter(partial(f.raw.read, 1024*1024), b'')zamiast łączenia takewhilei repeat.
Peter H.,
1
Och, częściowa funkcja, tak, to fajna drobna poprawka. Ponadto założyłem, że 1024 * 1024 zostanie scalony przez interpretera i potraktowany jako stały, ale to było na przeczucie, a nie dokumentację.
Michael Bacon,
90

Możesz wykonać podproces i uruchomić 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])
Lalafur Waage
źródło
6
jaka byłaby wersja systemu Windows?
SilentGhost
1
Możesz odnieść się do tego SO pytania dotyczącego tego. stackoverflow.com/questions/247234/…
Ólafur Waage
7
Rzeczywiście, w moim przypadku (Mac OS X) zajmuje to 0,13s w porównaniu do 0,5s do zliczenia liczby wierszy „dla xw pliku (...)”, w porównaniu do 1,0s liczenia powtarzających się wywołań do str.find lub mmap.find . (Plik, którego użyłem do przetestowania, ma 1,3 miliona linii.)
zginałem
1
Nie ma potrzeby angażowania w to powłoki. zredagowano odpowiedź i dodano przykładowy kod;
nosklo
2
Nie jest wieloplatformowy.
e-info128
42

Oto program w języku Python, który wykorzystuje bibliotekę wieloprocesową do dystrybucji liczenia linii między maszynami / rdzeniami. Mój test poprawia liczenie 20-milionowego pliku linii od 26 sekund do 7 sekund przy użyciu 8-rdzeniowego serwera Windows 64. Uwaga: nieużywanie mapowania pamięci znacznie spowalnia działanie.

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
źródło
Jak to działa z plikami znacznie większymi niż pamięć główna? na przykład plik 20 GB w systemie z 4 GB pamięci RAM i 2 rdzeniami
Brian Minton
Trudno to teraz przetestować, ale zakładam, że plik byłby wczytywany i wyrzucany na stronę.
Martlark
5
To całkiem fajny kod. Byłem zaskoczony, gdy stwierdziłem, że szybsze jest używanie wielu procesorów. Uznałem, że IO będzie wąskim gardłem. W starszych wersjach Pythona linia 21 wymaga int () jak chunk = int ((fSize / process)) + 1
Karl Henselin
czy ładuje cały plik do pamięci? co powiesz na większy pożar, którego rozmiar jest większy niż taran na komputerze?
pelos
Pliki są mapowane do pamięci wirtualnej, więc rozmiar pliku i ilość faktycznej pamięci zwykle nie jest ograniczeniem.
Martlark,
17

Jednowierszowe rozwiązanie bashowe podobne do tej odpowiedzi , wykorzystujące nowoczesną subprocess.check_outputfunkcję:

def line_count(filename):
    return int(subprocess.check_output(['wc', '-l', filename]).split()[0])
1 ''
źródło
Ta odpowiedź powinna zostać poddana pod głosowanie na wyższym miejscu w tym wątku dla użytkowników Linux / Unix. Pomimo większości preferencji w rozwiązaniu wieloplatformowym, jest to świetny sposób na Linux / Unix. W przypadku pliku csv o długości 184 milionów wierszy, z którego muszę próbkować dane, zapewnia on najlepsze środowisko wykonawcze. Inne rozwiązania czysto pythonowe zajmują średnio wc -lponad 100 sekund, podczas gdy wywołanie podprocesu zajmuje ~ 5 sekund.
Shan Dou
shell=Truejest szkodliwy dla bezpieczeństwa, lepiej go unikać.
Alexey Vazhnov
Sprawiedliwy punkt, zredagowany
1 ''
15

Chciałbym użyć metody obiektu pliku Pythona w readlinesnastępujący sposób:

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

To otwiera plik, tworzy listę linii w pliku, liczy długość listy, zapisuje ją w zmiennej i ponownie zamyka plik.

Daniel Lee
źródło
6
Chociaż jest to jeden z pierwszych sposobów, który przychodzi mi na myśl, prawdopodobnie nie jest on bardzo wydajny pod względem pamięci, zwłaszcza jeśli zliczanie wierszy w plikach do 10 GB (tak jak ja), co jest godną uwagi wadą.
Steen Schütt
@TimeSheep Czy to problem dotyczy plików z wieloma (powiedzmy miliardy) małymi liniami lub plikami, które mają wyjątkowo długie linie (powiedzmy, gigabajty na linię)?
Robert
Powód, dla którego pytam, wydaje się, że kompilator powinien być w stanie zoptymalizować to, nie tworząc listy pośredniej.
Robert
@dmityugov Dokumenty w języku Python xreadlinessą przestarzałe od wersji 2.3, ponieważ zwraca tylko iterator. for line in filejest podanym zamiennikiem. Zobacz: 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
źródło
12

Oto, czego używam, wydaje się całkiem czysty:

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

AKTUALIZACJA: Jest to nieznacznie szybsze niż używanie czystego Pythona, ale kosztem użycia pamięci. Podproces rozwidli nowy proces z takim samym obszarem pamięci, jak proces nadrzędny podczas wykonywania polecenia.

radtek
źródło
1
Na marginesie, to oczywiście nie będzie działać w systemie Windows.
Bram Vanroy,
core utils najwyraźniej zapewnia „wc” dla Windows stackoverflow.com/questions/247234/… . Możesz również użyć Linux-owej maszyny Wirtualnej w oknie systemu Windows, jeśli Twój kod będzie działał w systemie Linux w prod.
radtek
Lub WSL, wysoce zalecane w stosunku do dowolnej maszyny wirtualnej, jeśli takie rzeczy to jedyne, co robisz. :-)
Bram Vanroy
Tak, to działa. Nie jestem facetem od systemu Windows, ale z goolgowania nauczyłem się WSL = Windows Subsystem for Linux =)
radtek
3
python3.7: zwracane bajty podprocesu, więc kod wygląda następująco: int (subprocess.check_output (['wc', '-l', file_path]). decode ("utf-8"). lstrip (). split (" ") [0])
Alexey Alexeenka
11

Jest to najszybsza rzecz, jaką znalazłem, używając czystego pytona. Możesz użyć dowolnej ilości pamięci, ustawiając bufor, chociaż 2 ** 16 wydaje się być dobrym miejscem na moim komputerze.

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

Znalazłem odpowiedź tutaj Dlaczego czytanie linii ze standardowego wejścia w C ++ jest znacznie wolniejsze niż w Pythonie? i trochę poprawiłem. To bardzo dobra lektura, aby zrozumieć, jak szybko liczyć wiersze, choć wc -lnadal jest o 75% szybsza niż cokolwiek innego.

jeffpkamp
źródło
9

Uzyskałem niewielką (4-8%) poprawę w tej wersji, która ponownie wykorzystuje stały bufor, więc powinna unikać jakiegokolwiek narzutu pamięci lub GC:

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

Możesz bawić się z rozmiarem bufora i być może zauważyć niewielką poprawę.

Scott Persinger
źródło
Miły. Aby uwzględnić pliki, które nie kończą się na \ n, dodaj 1 poza pętlą, jeśli bufor i bufor [-1]! = '\ N'
ryuusenshi
Błąd: bufor w ostatniej rundzie może nie być czysty.
Jay
co jeśli pomiędzy buforami jedna część kończy się na \, a druga zaczyna się na n? który ominie jedną nową linię, przeskoczyłbym do zmiennych, aby zapisać koniec i początek każdej porcji, ale to może dodać więcej czasu do skryptu = (
pelos
9

Odpowiedź Kyle'a

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

jest prawdopodobnie najlepsza, alternatywą jest

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

Oto porównanie wydajności obu

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
źródło
9

Rozwiązanie jednoliniowe:

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

Mój fragment:

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

0 bar.txt
1000 command.txt
3 test_file.txt
1003 total
Egzorcysta
źródło
Dobry pomysł, niestety nie działa to jednak w systemie Windows.
Kim
3
jeśli chcesz być surferem pytona, pożegnaj się z oknami. Uwierz mi, że pewnego dnia mi podziękujesz.
Egzorcysta
6
Właśnie uznałem za godne uwagi, że będzie to działać tylko w systemie Windows. Wolę sam pracować na stosie linux / unix, ale pisząc oprogramowanie IMHO, należy wziąć pod uwagę efekty uboczne, które program może mieć, gdy działa w różnych systemach operacyjnych. Ponieważ OP nie wspomniał o swojej platformie i na wypadek, gdyby ktoś wpadł na to rozwiązanie przez Google i skopiował je (nieświadomy ograniczeń, jakie może mieć system Windows), chciałem dodać notatkę.
Kim
Nie możesz zapisać wyniku os.system()zmiennej i przetworzyć go w żaden sposób.
An
@ Masz rację, ale nie zadajesz pytania, czy to zapisuje, czy nie. Chyba rozumiesz kontekst.
TheExorcist
6

Aby wykonać powyższe metody, wypróbowałem wariant z modułem fileinput:

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

I przekazałem plik linii 60 mil do wszystkich wyżej wymienionych metod:

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

To dla mnie małe zaskoczenie, że dane wejściowe są tak złe i skalują się znacznie gorzej niż wszystkie inne metody ...

BandGap
źródło
5

Jak dla mnie ten wariant będzie najszybszy:

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

Powody: buforowanie jest szybsze niż czytanie linia po linii, a string.counttakże bardzo szybkie

Mykoła Chareczko
źródło
1
Ale czy to jest? Przynajmniej na OSX / python2.5 wersja OP jest nadal o około 10% szybsza według timeit.py.
dF.
Co jeśli ostatni wiersz nie kończy się na „\ n”?
tzot
1
Nie wiem, jak to przetestowałeś, dF, ale na moim komputerze jest ~ 2,5 razy wolniejszy niż jakakolwiek inna opcja.
SilentGhost
34
Oświadczasz, że będzie najszybszy, a następnie stwierdzasz, że go nie testowałeś. Niezbyt naukowe, prawda? :)
Ólafur Waage
Zobacz rozwiązanie i statystyki dostarczone przez Ryana Ginstroma poniżej. Zobacz także komentarz JF Sebastiana i link do tej samej odpowiedzi.
SherylHohman
5

Ten kod jest krótszy i bardziej przejrzysty. To chyba najlepszy sposób:

num_lines = open('yourfile.ext').read().count('\n')
Texom512
źródło
6
Powinieneś także zamknąć plik.
rsm
6
Załaduje cały plik do pamięci.
Ivelin
nie najlepiej, gdy potrzebujesz wydajności na dużych plikach
mabraham
4

Zmodyfikowałem przypadek bufora w następujący sposób:

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

Teraz liczone są także puste pliki i ostatni wiersz (bez \ n).

Manekin
źródło
Może także wyjaśnij (lub dodaj komentarz w kodzie), co zmieniłeś i po co;). Może znacznie ułatwić ludziom kodowanie (zamiast „analizowania” kodu w mózgu).
Styxxy,
Myślę, że optymalizacja pętli pozwala Pythonowi na wyszukiwanie zmiennych lokalnych w read_f, python.org/doc/essays/list2str
The Red Pea
3

A co z tym

def file_len(fname):
  counts = itertools.count()
  with open(fname) as f: 
    for _ in f: counts.next()
  return counts.next()
odwl
źródło
3

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

pyanon
źródło
Daje to liczbę -1 prawdziwej wartości.
Borealis
Opcjonalnym drugim argumentem enumerate()jest liczenie początkowe zgodnie z docs.python.org/2/library/functions.html#enumerate
MarkHu
3
print open('file.txt', 'r').read().count("\n") + 1
Andrés Torres
źródło
3
def line_count(path):
    count = 0
    with open(path) as lines:
        for count, l in enumerate(lines, start=1):
            pass
    return count
mdwhatcott
źródło
3

Jeśli chcesz taniej uzyskać liczbę wierszy w Pythonie w Linuksie, polecam tę metodę:

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

ścieżka_pliku może być zarówno ścieżką abstrakcyjną, jak i ścieżką względną. Mam nadzieję, że to może pomóc.

Lerner Zhang
źródło
2

Co powiesz na to?

import fileinput
import sys

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

fileinput.close()
print counter
leba-lev
źródło
2

Co powiesz na ten liniowiec:

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

Zastosowanie tej metody zajmuje 0,003 s, aby zmierzyć czas w pliku linii 3900

def c():
  import time
  s = time.time()
  file_length = len(open('myfile.txt','r').read().split('\n'))
  print time.time() - s
onetwopunch
źródło
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
źródło
Czy mógłbyś wyjaśnić, co jest z nią nie tak, jeśli uważasz, że to źle? To zadziałało dla mnie. Dzięki!
jciloa,
Byłbym również zainteresowany tym, dlaczego ta odpowiedź została również odrzucona. Powtarza plik po liniach i sumuje je. Podoba mi się, jest krótki i do rzeczy, co jest z nim nie tak?
procesor
2

Prosta metoda:

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
źródło
3
W tym przykładzie plik nie jest zamknięty.
Maciej M
9
OP chciał czegoś wydajnego pod względem pamięci. To zdecydowanie nie to.
Andy Carlson,
1

wynikiem otwarcia pliku jest iterator, który można przekonwertować na sekwencję o długości:

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

jest to bardziej zwięzłe niż twoja wyraźna pętla i pozwala uniknąć enumerate.

Andrew Jaffe
źródło
10
co oznacza, że ​​plik 100 Mb będzie musiał zostać odczytany do pamięci.
SilentGhost
tak, dobra uwaga, chociaż zastanawiam się nad różnicą prędkości (w przeciwieństwie do pamięci). Prawdopodobnie możliwe jest utworzenie iteratora, który to robi, ale myślę, że byłoby to równoważne z twoim rozwiązaniem.
Andrew Jaffe,
6
-1, to nie tylko pamięć, ale konieczność konstruowania listy w pamięci.
orip
0

Możesz użyć os.pathmodułu w następujący sposób:

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

, gdzie Filenamejest bezwzględna ścieżka do pliku.

Zwycięzca
źródło
1
Co to ma wspólnego z tą odpowiedzią os.path?
moi
0

Jeśli plik zmieści się w pamięci, to

with open(fname) as f:
    count = len(f.read().split(b'\n')) - 1
Karthik
źródło