Wie kann man die Zeilenanzahl einer großen Datei in Python billig ermitteln?

1011

Ich muss eine Zeilenanzahl einer großen Datei (Hunderttausende von Zeilen) in Python erhalten. Was ist der effizienteste Weg sowohl in Bezug auf das Gedächtnis als auch in Bezug auf die Zeit?

Im Moment mache ich:

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

ist es möglich, es besser zu machen?

SilentGhost
quelle
7
Benötigen Sie eine genaue Zeilenanzahl oder reicht eine Annäherung aus?
Pico
43
Ich würde i = -1 vor for-Schleife hinzufügen, da dieser Code für leere Dateien nicht funktioniert.
Maciek Sawicki
12
@Legend: Ich wette, Pico denkt, erhalte die Dateigröße (mit seek (0,2) oder Äquiv.), Dividiere durch die ungefähre Zeilenlänge. Sie können am Anfang einige Zeilen lesen, um die durchschnittliche Zeilenlänge zu erraten.
Anne
32
enumerate(f, 1)und den i + 1?
Ian Mackinnon
4
@IanMackinnon Funktioniert für leere Dateien, aber Sie müssen i vor der for-Schleife auf 0 initialisieren .
Scai

Antworten:

357

Besser geht es nicht.

Schließlich muss jede Lösung die gesamte Datei lesen, herausfinden, wie viele \nSie haben, und dieses Ergebnis zurückgeben.

Haben Sie eine bessere Möglichkeit, dies zu tun, ohne die gesamte Datei zu lesen? Nicht sicher ... Die beste Lösung ist immer E / A-gebunden. Das Beste, was Sie tun können, ist sicherzustellen, dass Sie keinen unnötigen Speicher verwenden, aber es sieht so aus, als hätten Sie diesen abgedeckt.

Yuval Adam
quelle
7
Genau, sogar WC liest die Datei durch, aber in C und es ist wahrscheinlich ziemlich optimiert.
Ólafur Waage
6
Soweit ich weiß, erfolgt die Python-Datei IO auch über C. docs.python.org/library/stdtypes.html#file-objects
Tomalak
9
@Tomalak Das ist ein roter Hering. Während Python und WC möglicherweise dieselben Systemaufrufe ausgeben, hat Python einen Opcode-Versandaufwand, den WC nicht hat.
Bobpoekert
4
Sie können eine Zeilenanzahl durch Abtasten approximieren. Es kann tausendfach schneller sein. Siehe: documentroot.com/2011/02/…
Erik Aronesty
4
Andere Antworten scheinen darauf hinzudeuten, dass diese kategoriale Antwort falsch ist, und sollten daher gelöscht und nicht als akzeptiert beibehalten werden.
Skippy le Grand Gourou
625

Eine Zeile, wahrscheinlich ziemlich schnell:

num_lines = sum(1 for line in open('myfile.txt'))
Kyle
quelle
8
Es ist ähnlich wie die Summe (Folge von 1). Jede Zeile zählt als 1. >>> [1 für Zeile im Bereich (10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] >>> Summe (1 für Linie im Bereich (10)) 10 >>>
James Sapam
4
num_lines = sum (1 für offene Zeile ('myfile.txt') wenn line.rstrip ()) für leere Zeilen filtern
Honghe.Wu
61
Wird diese beim Öffnen einer Datei automatisch geschlossen, sobald wir alle Elemente durchlaufen haben? Muss 'close ()' sein? Ich denke, wir können 'with open ()' in dieser kurzen Anweisung nicht verwenden, oder?
Mannaggia
16
@Mannaggia Sie haben Recht, es ist besser, 'mit open (Dateiname)' zu verwenden, um sicherzustellen, dass die Datei geschlossen wird, wenn Sie fertig sind, und noch besser ist dies innerhalb eines Try-Except-Blocks, in dem die Ausnahme und IOError ausgelöst werden, wenn Die Datei kann nicht geöffnet werden.
BoltzmannBrain
17
Eine andere Sache zu beachten: Dies ist ~ 0,04-0,05 Sekunden langsamer als die, die das ursprüngliche Problem auf einer
300.000-
202

Ich glaube, dass eine Speicherzuordnungsdatei die schnellste Lösung sein wird. Ich habe vier Funktionen ausprobiert: die vom OP ( opcount) gepostete Funktion ; eine einfache Iteration über die Zeilen in der Datei ( simplecount); readline mit einem speicherabgebildeten Feld (mmap) ( mapcount); und die von Mykola Kharechko ( bufcount) angebotene Pufferleselösung .

Ich habe jede Funktion fünf Mal ausgeführt und die durchschnittliche Laufzeit für eine Textdatei mit 1,2 Millionen Zeilen berechnet.

Windows XP, Python 2.5, 2 GB RAM, 2 GHz AMD-Prozessor

Hier sind meine Ergebnisse:

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

Edit : Zahlen für Python 2.6:

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

Daher scheint die Pufferlesestrategie für Windows / Python 2.6 die schnellste zu sein

Hier ist der Code:

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
quelle
1
Die gesamte Speicherzuordnungsdatei wird nicht in den Speicher geladen. Sie erhalten einen virtuellen Speicherplatz, den das Betriebssystem nach Bedarf in den Arbeitsspeicher und aus dem Arbeitsspeicher austauscht. So werden sie unter Windows behandelt: msdn.microsoft.com/en-us/library/ms810613.aspx
Ryan Ginstrom
1
Entschuldigung, hier ist eine allgemeinere Referenz zu Dateien mit Speicherzuordnung: en.wikipedia.org/wiki/Memory-mapped_file Und danke für die Abstimmung. :)
Ryan Ginstrom
1
Obwohl es sich nur um einen virtuellen Speicher handelt, ist es genau das, was diesen Ansatz einschränkt und daher für große Dateien nicht funktioniert. Ich habe es mit ~ 1,2 Gb Datei mit über 10 Millionen versucht. Zeilen (wie mit wc -l erhalten) und haben gerade einen Windows-Fehler erhalten: [Fehler 8] Es ist nicht genügend Speicher verfügbar, um diesen Befehl zu verarbeiten. Dies ist natürlich ein Randfall.
SilentGhost
6
+1 für echte Zeitdaten. Wissen wir, ob die Puffergröße von 1024 * 1024 optimal ist oder ob es eine bessere gibt?
Kiv
28
Es scheint, dass dies wccount()der schnellste ist gist.github.com/0ac760859e614cd03652
jfs
133

Ich musste dies auf eine ähnliche Frage posten, bis mein Reputationswert ein wenig sprang (danke an jeden, der mich gestoßen hat!).

Alle diese Lösungen ignorieren eine Möglichkeit, diesen Lauf erheblich zu beschleunigen, nämlich die Verwendung der ungepufferten (Roh-) Schnittstelle, die Verwendung von Bytearrays und die eigene Pufferung. (Dies gilt nur in Python 3. In Python 2 wird die Rohschnittstelle möglicherweise standardmäßig verwendet oder nicht, in Python 3 wird jedoch standardmäßig Unicode verwendet.)

Unter Verwendung einer modifizierten Version des Timing-Tools ist der folgende Code meiner Meinung nach schneller (und geringfügig pythonischer) als jede der angebotenen Lösungen:

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

Mit einer separaten Generatorfunktion wird ein Smidge schneller ausgeführt:

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 )

Dies kann vollständig mit Generatorausdrücken inline unter Verwendung von itertools durchgeführt werden, aber es sieht ziemlich seltsam aus:

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 )

Hier sind meine Timings:

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
quelle
20
Ich arbeite mit Dateien mit mehr als 100 GB und Ihre Rawgencounts sind die einzige realisierbare Lösung, die ich bisher gesehen habe. Vielen Dank!
Soungalo
1
ist wccountin dieser Tabelle für das Subprozess-Shell- wcTool?
Anentropic
1
fand dies in einem anderen Kommentar, ich denke, es ist dann gist.github.com/zed/0ac760859e614cd03652
Anentropic
3
Danke @ Michael-Bacon, es ist eine wirklich schöne Lösung. Sie können die rawincountLösung weniger seltsam aussehen lassen, indem Sie bufgen = iter(partial(f.raw.read, 1024*1024), b'')statt kombinieren takewhileund repeat.
Peter H.
1
Oh, Teilfunktion, ja, das ist eine nette kleine Verbesserung. Außerdem nahm ich an, dass der 1024 * 1024 vom Interpreter zusammengeführt und als Konstante behandelt werden würde, aber das war ahnungslos keine Dokumentation.
Michael Bacon
90

Sie können einen Unterprozess ausführen und ausführen 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
quelle
6
Was wäre die Windows-Version davon?
SilentGhost
1
Sie können sich diesbezüglich auf diese SO-Frage beziehen. stackoverflow.com/questions/247234/…
Ólafur Waage
7
In meinem Fall (Mac OS X) dauert dies 0,13 Sekunden gegenüber 0,5 Sekunden, um die Anzahl der Zeilen zu zählen, die "für x in Datei (...)" erzeugt, im Vergleich zu 1,0 Sekunden, in denen wiederholte Aufrufe von str.find oder mmap.find gezählt werden . (Die Datei, mit der ich das getestet habe, hat 1,3 Millionen Zeilen.)
Bendin
1
Keine Notwendigkeit, die Shell darauf einzubeziehen. bearbeitete Antwort und hinzugefügter Beispielcode;
Nosklo
2
Ist nicht plattformübergreifend.
E-Info128
42

Hier ist ein Python-Programm, mit dem die Multiprozessor-Bibliothek verwendet wird, um die Zeilenzählung auf Maschinen / Kerne zu verteilen. Mein Test verbessert das Zählen einer 20-Millionen-Zeilendatei von 26 Sekunden auf 7 Sekunden unter Verwendung eines 8-Kern-Windows 64-Servers. Hinweis: Wenn Sie keine Speicherzuordnung verwenden, werden die Dinge viel langsamer.

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
quelle
Wie funktioniert das mit Dateien, die viel größer als der Hauptspeicher sind? Zum Beispiel eine 20-GB-Datei auf einem System mit 4 GB RAM und 2 Kernen
Brian Minton
Schwer zu testen, aber ich gehe davon aus, dass die Datei ein- und ausgeblendet wird.
Martlark
5
Das ist ziemlich ordentlicher Code. Ich war überrascht, dass es schneller ist, mehrere Prozessoren zu verwenden. Ich dachte mir, dass das IO der Engpass sein würde. In älteren Python-Versionen benötigt Zeile 21 int () wie chunk = int ((fSize / Prozesse)) + 1
Karl Henselin
lädt es die gesamte Datei in den Speicher? Was ist mit einem größeren Feuer, bei dem die Größe größer ist als der RAM auf dem Computer?
Pelos
Die Dateien werden dem virtuellen Speicher zugeordnet, sodass die Größe der Datei und die Größe des tatsächlichen Speichers normalerweise keine Einschränkung darstellen.
Martlark
17

Eine einzeilige Bash-Lösung ähnlich dieser Antwort unter Verwendung der modernen subprocess.check_outputFunktion:

def line_count(filename):
    return int(subprocess.check_output(['wc', '-l', filename]).split()[0])
1 ''
quelle
Diese Antwort sollte für Linux / Unix-Benutzer an einer höheren Stelle in diesem Thread bewertet werden. Trotz der meisten Präferenzen in einer plattformübergreifenden Lösung ist dies eine hervorragende Möglichkeit unter Linux / Unix. Für eine CSV-Datei mit 184 Millionen Zeilen, aus der ich Daten abtasten muss, bietet sie die beste Laufzeit. Andere reine Python-Lösungen dauern durchschnittlich mehr als 100 Sekunden, während der Unterprozessaufruf von wc -l~ 5 Sekunden dauert.
Shan Dou
shell=Trueist schlecht für die Sicherheit, es ist besser, es zu vermeiden.
Alexey Vazhnov
Fair Point, bearbeitet
1.
15

Ich würde Pythons Dateiobjektmethode readlineswie folgt verwenden:

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

Dies öffnet die Datei, erstellt eine Liste von Zeilen in der Datei, zählt die Länge der Liste, speichert diese in einer Variablen und schließt die Datei erneut.

Daniel Lee
quelle
6
Dies ist zwar eine der ersten Möglichkeiten, die mir in den Sinn kommen, aber wahrscheinlich nicht sehr speichereffizient, insbesondere wenn Zeilen in Dateien mit bis zu 10 GB gezählt werden (wie ich), was ein bemerkenswerter Nachteil ist.
Steen Schütt
@TimeSheep Ist dies ein Problem für Dateien mit vielen (z. B. Milliarden) kleinen Zeilen oder für Dateien mit extrem langen Zeilen (z. B. Gigabyte pro Zeile)?
Robert
Der Grund, den ich frage, ist, dass der Compiler anscheinend in der Lage sein sollte, dies zu optimieren, indem er keine Zwischenliste erstellt.
Robert
@dmityugov Per Python-Dokumente sind xreadlinesseit 2.3 veraltet, da nur ein Iterator zurückgegeben wird. for line in fileist der angegebene Ersatz. Siehe: 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
quelle
12

Folgendes benutze ich, scheint ziemlich sauber zu sein:

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: Dies ist geringfügig schneller als die Verwendung von reinem Python, jedoch auf Kosten der Speichernutzung. Der Unterprozess gibt einen neuen Prozess mit dem gleichen Speicherbedarf wie der übergeordnete Prozess aus, während er Ihren Befehl ausführt.

radtek
quelle
1
Nur als Randnotiz, dies funktioniert natürlich nicht unter Windows.
Bram Vanroy
Core Utils bietet anscheinend "wc" für Windows stackoverflow.com/questions/247234/… . Sie können auch eine Linux-VM in Ihrer Windows-Box verwenden, wenn Ihr Code unter Linux in Prod ausgeführt wird.
Radtek
Oder WSL, die über jede VM sehr gut beraten ist, wenn Sie nur solche Dinge tun. :-)
Bram Vanroy
Ja das funktioniert. Ich bin kein Windows-Typ, aber durch das Spielen habe ich WSL = Windows Subsystem für Linux gelernt =)
radtek
3
python3.7: Subprozess-Rückgabebytes, daher sieht der Code folgendermaßen aus: int (subprocess.check_output (['wc', '-l', Dateipfad]). decode ("utf-8"). lstrip (). split (" ") [0])
Alexey Alexeenka
11

Dies ist das schnellste, was ich mit reinem Python gefunden habe. Sie können beliebig viel Speicher verwenden, indem Sie den Puffer einstellen, obwohl 2 ** 16 ein Sweet Spot auf meinem Computer zu sein scheint.

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

Ich habe die Antwort hier gefunden. Warum ist das Lesen von Zeilen aus stdin in C ++ viel langsamer als in Python? und optimierte es nur ein kleines bisschen. Es ist eine sehr gute Lektüre, um zu verstehen, wie man Zeilen schnell zählt, obwohl wc -les immer noch etwa 75% schneller ist als alles andere.

jeffpkamp
quelle
9

Ich habe mit dieser Version eine kleine Verbesserung (4-8%) erzielt, bei der ein konstanter Puffer wiederverwendet wird, um Speicher- oder GC-Overhead zu vermeiden:

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

Sie können mit der Puffergröße herumspielen und vielleicht eine kleine Verbesserung feststellen.

Scott Persinger
quelle
Nett. Um Dateien zu berücksichtigen, die nicht mit \ n enden, fügen Sie 1 außerhalb der Schleife hinzu, wenn buffer und buffer [-1]! = '\ N'
ryuusenshi
Ein Fehler: Der Puffer in der letzten Runde ist möglicherweise nicht sauber.
Jay
Was ist, wenn zwischen den Puffern ein Teil mit \ endet und der andere Teil mit n beginnt? das wird eine neue Zeile dort vermissen, ich würde zu Variablen schäumen, um das Ende und den Anfang jedes Blocks zu speichern, aber das könnte mehr Zeit zum Skript hinzufügen = (
pelos
9

Kyles Antwort

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

ist wohl am besten, eine alternative dafür ist

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

Hier ist der Vergleich der Leistung von beiden

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
quelle
9

Einzeilige Lösung:

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

Mein Ausschnitt:

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

0 bar.txt
1000 command.txt
3 test_file.txt
1003 total
Der Exorzist
quelle
Gute Idee, leider funktioniert dies unter Windows nicht.
Kim
3
Wenn Sie Python-Surfer werden möchten, verabschieden Sie sich von Windows. Glauben Sie mir, Sie werden mir eines Tages danken.
TheExorcist
6
Ich fand es nur bemerkenswert, dass dies nur unter Windows funktioniert. Ich arbeite lieber selbst an einem Linux / Unix-Stack, aber beim Schreiben von Software IMHO sollte man die Nebenwirkungen berücksichtigen, die ein Programm haben kann, wenn es unter verschiedenen Betriebssystemen ausgeführt wird. Da das OP seine Plattform nicht erwähnte und falls jemand über Google auf diese Lösung zugreift und sie kopiert (ohne die Einschränkungen eines Windows-Systems zu kennen), wollte ich den Hinweis hinzufügen.
Kim
Sie können die Ausgabe der os.system()Variablen sowieso nicht speichern und nachbearbeiten.
Ein Se
@AnSe Sie sind richtig, aber die Frage wird nicht gestellt, ob es speichert oder nicht. Ich denke, Sie verstehen den Kontext.
TheExorcist
6

Um die oben genannten Methoden zu vervollständigen, habe ich eine Variante mit dem Dateieingabemodul ausprobiert:

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

Und eine 60-mil-Zeilendatei an alle oben genannten Methoden übergeben:

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

Es ist eine kleine Überraschung für mich, dass der Dateieingang so schlecht ist und weitaus schlechter skaliert als alle anderen Methoden ...

Bandlücke
quelle
5

Für mich wird diese Variante die schnellste sein:

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

Gründe: Pufferung schneller als zeilenweises Lesen und string.countauch sehr schnell

Mykola Kharechko
quelle
1
Aber ist es? Zumindest unter OSX / python2.5 ist die OP-Version laut timeit.py immer noch etwa 10% schneller.
dF.
Was ist, wenn die letzte Zeile nicht mit '\ n' endet?
tzot
1
Ich weiß nicht, wie Sie es getestet haben, dF, aber auf meinem Computer ist es ~ 2,5-mal langsamer als jede andere Option.
SilentGhost
34
Sie geben an, dass es das schnellste ist, und geben dann an, dass Sie es nicht getestet haben. Nicht sehr wissenschaftlich, oder? :)
Ólafur Waage
Siehe Lösung und Statistiken von Ryan Ginstrom unten. Lesen Sie auch den Kommentar von JF Sebastian und den Link zu derselben Antwort.
SherylHohman
5

Dieser Code ist kürzer und klarer. Es ist wahrscheinlich der beste Weg:

num_lines = open('yourfile.ext').read().count('\n')
Texom512
quelle
6
Sie sollten die Datei auch schließen.
rsm
6
Es wird die gesamte Datei in den Speicher geladen.
Ivelin
Nicht das Beste, wenn Sie Leistung für große Dateien benötigen
Mabraham
4

Ich habe den Pufferfall folgendermaßen geändert:

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

Jetzt werden auch leere Dateien und die letzte Zeile (ohne \ n) gezählt.

Dummy
quelle
Vielleicht erklären Sie auch (oder fügen Sie einen Kommentar in den Code ein), was Sie geändert haben und wofür;). Könnte den Leuten viel mehr Einblick in Ihren Code geben (anstatt den Code im Gehirn zu "analysieren").
Styxxy
Die Schleifenoptimierung ermöglicht Python meiner Meinung nach die Suche nach lokalen Variablen unter read_f, python.org/doc/essays/list2str
The Red Pea
3

Was ist damit?

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

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

Pyanon
quelle
Dies ergibt die Anzahl -1 des wahren Wertes.
Borealis
Optionales zweites Argument für enumerate()ist Startzählung
MarkHu
3
print open('file.txt', 'r').read().count("\n") + 1
Andrés Torres
quelle
3
def line_count(path):
    count = 0
    with open(path) as lines:
        for count, l in enumerate(lines, start=1):
            pass
    return count
mdwhatcott
quelle
3

Wenn man die Zeilenanzahl in Python unter Linux billig erhalten möchte, empfehle ich diese Methode:

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

Dateipfad kann sowohl ein abstrakter Dateipfad als auch ein relativer Pfad sein. Hoffe das kann helfen.

Lerner Zhang
quelle
2

Wie wäre es damit?

import fileinput
import sys

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

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

Wie wäre es mit diesem Einzeiler:

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

Mit dieser Methode dauert es 0,003 Sekunden, um die Zeit in einer 3900-Zeilendatei zu messen

def c():
  import time
  s = time.time()
  file_length = len(open('myfile.txt','r').read().split('\n'))
  print time.time() - s
onetwopunch
quelle
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
quelle
Könnten Sie bitte erklären, was daran falsch ist, wenn Sie denken, dass es falsch ist? Es hat bei mir funktioniert. Vielen Dank!
Jciloa
Mich würde interessieren, warum diese Antwort auch abgelehnt wurde. Es durchläuft die Datei zeilenweise und fasst sie zusammen. Ich mag es, es ist kurz und auf den Punkt, was ist daran falsch?
Cessor
2

Einfache Methode:

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
quelle
3
In diesem Beispiel wird die Datei nicht geschlossen.
Maciej M
9
OP wollte etwas speichereffizientes. Das ist es definitiv nicht.
Andy Carlson
1

Das Ergebnis des Öffnens einer Datei ist ein Iterator, der in eine Sequenz konvertiert werden kann, die eine Länge hat:

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

Dies ist prägnanter als Ihre explizite Schleife und vermeidet das enumerate.

Andrew Jaffe
quelle
10
Dies bedeutet, dass 100-MB-Dateien in den Speicher eingelesen werden müssen.
SilentGhost
Ja, guter Punkt, obwohl ich mich über den Geschwindigkeitsunterschied (im Gegensatz zum Gedächtnis) wundere. Es ist wahrscheinlich möglich, einen Iterator zu erstellen, der dies tut, aber ich denke, er wäre gleichbedeutend mit Ihrer Lösung.
Andrew Jaffe
6
-1, es ist nicht nur der Speicher, sondern es muss die Liste im Speicher erstellt werden.
Orip
0

Sie können das os.pathModul folgendermaßen verwenden:

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

, wo Filenameist der absolute Pfad der Datei.

Sieger
quelle
1
Was hat diese Antwort damit zu tun os.path?
Moi
0

Wenn die Datei in den Speicher passt, dann

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