Comment obtenir le nombre de lignes d'un gros fichier à moindre coût en Python?

1012

J'ai besoin d'obtenir le nombre de lignes d'un gros fichier (des centaines de milliers de lignes) en python. Quelle est la manière la plus efficace à la fois en termes de mémoire et de temps?

En ce moment je fais:

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

est-il possible de faire mieux?

SilentGhost
la source
7
Avez-vous besoin d'un nombre de lignes exact ou une approximation suffira-t-elle?
pico
43
J'ajouterais i = -1 avant pour la boucle, car ce code ne fonctionne pas pour les fichiers vides.
Maciek Sawicki
12
@Legend: Je parie que pico pense, obtenez la taille du fichier (avec la recherche (0,2) ou l'équiv), divisez par la longueur approximative de la ligne. Vous pouvez lire quelques lignes au début pour deviner la longueur moyenne des lignes.
Anne
32
enumerate(f, 1)et abandonner le i + 1?
Ian Mackinnon
4
@IanMackinnon Fonctionne pour les fichiers vides, mais vous devez initialiser i à 0 avant la boucle for.
scai

Réponses:

357

Vous ne pouvez pas faire mieux que ça.

Après tout, toute solution devra lire l'intégralité du fichier, déterminer combien \nvous en avez et retourner ce résultat.

Avez-vous une meilleure façon de procéder sans lire l'intégralité du fichier? Pas sûr ... La meilleure solution sera toujours liée aux E / S, le mieux que vous puissiez faire est de vous assurer que vous n'utilisez pas de mémoire inutile, mais il semble que vous ayez couvert cela.

Yuval Adam
la source
7
Exactement, même WC lit le fichier, mais en C et il est probablement assez optimisé.
Ólafur Waage
6
Pour autant que je sache, le fichier Python IO se fait également via C. docs.python.org/library/stdtypes.html#file-objects
Tomalak
9
@Tomalak C'est un hareng rouge. Alors que python et wc peuvent émettre les mêmes appels système, python a une surcharge de répartition d'opcode que wc n'a pas.
bobpoekert
4
Vous pouvez approximer un nombre de lignes par échantillonnage. Cela peut être des milliers de fois plus rapide. Voir: documentroot.com/2011/02/…
Erik Aronesty
4
D'autres réponses semblent indiquer que cette réponse catégorique est fausse et devrait donc être supprimée plutôt que conservée comme acceptée.
Skippy le Grand Gourou du
625

Une ligne, probablement assez rapide:

num_lines = sum(1 for line in open('myfile.txt'))
Kyle
la source
8
son semblable à la somme (séquence de 1) chaque ligne compte pour 1. >>> [1 pour la ligne dans la plage (10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] >>> somme (1 pour la ligne dans la plage (10)) 10 >>>
James Sapam
4
num_lines = sum (1 pour la ligne ouverte ('monfichier.txt') si line.rstrip ()) pour filtrer les lignes vides
Honghe.Wu
61
lorsque nous ouvrons un fichier, celui-ci sera-t-il fermé automatiquement une fois que nous aurons répété tous les éléments? Est-il nécessaire de «fermer ()»? Je pense que nous ne pouvons pas utiliser «avec open ()» dans cette courte déclaration, non?
Mannaggia
16
@Mannaggia vous avez raison, il serait préférable d'utiliser 'avec open (nom de fichier)' pour être sûr que le fichier se ferme une fois terminé, et c'est encore mieux de le faire dans un bloc try-except, où l'exception IOError est levée si le fichier ne peut pas être ouvert.
BoltzmannBrain
17
Une autre chose à noter: c'est ~ 0.04-0.05 secondes plus lent que celui que le problème d'origine a donné sur un fichier texte de 300 mille lignes
andrew
202

Je pense qu'un fichier mappé en mémoire sera la solution la plus rapide. J'ai essayé quatre fonctions: la fonction publiée par l'OP ( opcount); une simple itération sur les lignes du fichier ( simplecount); readline avec un fichier mappé en mémoire (mmap) ( mapcount); et la solution de lecture tampon proposée par Mykola Kharechko (bufcount ).

J'ai exécuté chaque fonction cinq fois et calculé la durée d'exécution moyenne d'un fichier texte de 1,2 million de lignes.

Windows XP, Python 2.5, 2 Go de RAM, processeur AMD 2 GHz

Voici mes résultats:

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

Edit : nombres pour Python 2.6:

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

La stratégie de lecture du tampon semble donc être la plus rapide pour Windows / Python 2.6

Voici le 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
la source
1
L'ensemble du fichier mappé en mémoire n'est pas chargé en mémoire. Vous obtenez un espace de mémoire virtuelle, que le système d'exploitation échange dans et hors de la RAM selon les besoins. Voici comment ils sont gérés sous Windows: msdn.microsoft.com/en-us/library/ms810613.aspx
Ryan Ginstrom
1
Désolé, voici une référence plus générale sur les fichiers mappés en mémoire: en.wikipedia.org/wiki/Memory-mapped_file Et merci pour le vote. :)
Ryan Ginstrom
1
Même s'il ne s'agit que d'une mémoire virtuelle, c'est précisément ce qui limite cette approche et, par conséquent, cela ne fonctionnera pas pour les fichiers volumineux. Je l'ai essayé avec un fichier ~ 1,2 Go avec plus de 10 mln. (comme obtenu avec wc -l) et vient de recevoir une erreur WindowsError: [Erreur 8] Pas assez de stockage disponible pour traiter cette commande. bien sûr, c'est un cas de bord.
SilentGhost
6
+1 pour les données de synchronisation réelles. Savons-nous si la taille du tampon de 1024 * 1024 est optimale, ou y en a-t-il une meilleure?
Kiv
28
Il semble que ce wccount()soit le gist.github.com/0ac760859e614cd03652
jfs
133

J'ai dû poster ceci sur une question similaire jusqu'à ce que mon score de réputation ait un peu augmenté (merci à celui qui m'a cogné!).

Toutes ces solutions ignorent une façon d'accélérer considérablement l'exécution, à savoir en utilisant l'interface non tamponnée (brute), en utilisant des tableaux de bord et en effectuant votre propre mise en mémoire tampon. (Cela ne s'applique qu'à Python 3. Dans Python 2, l'interface brute peut ou non être utilisée par défaut, mais dans Python 3, vous utiliserez par défaut Unicode.)

En utilisant une version modifiée de l'outil de synchronisation, je pense que le code suivant est plus rapide (et légèrement plus pythonique) que toutes les solutions proposées:

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

En utilisant une fonction de générateur séparée, cela fonctionne plus rapidement:

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 )

Cela peut être fait complètement avec des expressions de générateurs en ligne à l'aide d'itertools, mais cela devient assez étrange:

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 )

Voici mes horaires:

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
la source
20
Je travaille avec des fichiers de 100 Go +, et vos données brutes sont la seule solution possible que j'ai vue jusqu'à présent. Merci!
soungalo
1
est wccountdans ce tableau pour l' wcoutil shell de sous-processus ?
Anentropic
1
trouvé cela dans un autre commentaire, je suppose que c'est alors gist.github.com/zed/0ac760859e614cd03652
Anentropic
3
Merci @ michael-bacon, c'est une très bonne solution. Vous pouvez rendre la rawincountsolution moins étrange en utilisant bufgen = iter(partial(f.raw.read, 1024*1024), b'')au lieu de combiner takewhileet repeat.
Peter H.
1
Oh, fonction partielle, oui, c'est un joli petit ajustement. En outre, j'ai supposé que le 1024 * 1024 serait fusionné par l'interpréteur et traité comme une constante, mais c'était sur l'intuition et non sur la documentation.
Michael Bacon
90

Vous pouvez exécuter un sous-processus et exécuter 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
la source
6
quelle en serait la version windows?
SilentGhost
1
Vous pouvez vous référer à cette question SO à ce sujet. stackoverflow.com/questions/247234/…
Ólafur Waage
7
En effet, dans mon cas (Mac OS X) cela prend 0,13s contre 0,5s pour compter le nombre de lignes "pour x dans le fichier (...)" produit, contre 1,0s pour compter les appels répétés vers str.find ou mmap.find . (Le fichier que j'ai utilisé pour tester cela contient 1,3 million de lignes.)
bendin
1
Pas besoin d'impliquer le shell là-dessus. réponse modifiée et exemple de code ajouté;
nosklo
2
N'est pas multiplateforme.
e-info128
42

Voici un programme python pour utiliser la bibliothèque multiprocesseurs pour distribuer le comptage de lignes sur les machines / cœurs. Mon test améliore le comptage d'un fichier de 20 millions de lignes de 26 secondes à 7 secondes en utilisant un serveur Windows 64 à 8 cœurs. Remarque: ne pas utiliser le mappage de mémoire rend les choses beaucoup plus lentes.

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
la source
Comment cela fonctionne-t-il avec des fichiers beaucoup plus volumineux que la mémoire principale? par exemple un fichier de 20 Go sur un système avec 4 Go de RAM et 2 cœurs
Brian Minton
Difficile à tester maintenant, mais je suppose que cela ferait entrer et sortir le fichier.
Martlark
5
C'est un code assez soigné. J'ai été surpris de constater qu'il est plus rapide d'utiliser plusieurs processeurs. J'ai pensé que l'IO serait le goulot d'étranglement. Dans les anciennes versions de Python, la ligne 21 a besoin de int () comme chunk = int ((fSize / processus)) + 1
Karl Henselin
charge-t-il tout le fichier dans la mémoire? que diriez-vous d'un plus grand feu où la taille est plus grande que le bélier sur l'ordinateur?
pelos
Les fichiers sont mappés dans la mémoire virtuelle, donc la taille du fichier et la quantité de mémoire réelle ne sont généralement pas une restriction.
Martlark
17

Une solution bash d'une ligne similaire à cette réponse , utilisant la subprocess.check_outputfonction moderne :

def line_count(filename):
    return int(subprocess.check_output(['wc', '-l', filename]).split()[0])
1''
la source
Cette réponse devrait être votée à une place plus élevée dans ce fil pour les utilisateurs Linux / Unix. Malgré les préférences de la majorité dans une solution multiplateforme, c'est un excellent moyen sur Linux / Unix. Pour un fichier csv de 184 millions de lignes dont je dois échantillonner les données, il fournit le meilleur temps d'exécution. D'autres solutions python pures prennent en moyenne 100+ secondes alors que l'appel de sous-processus wc -lprend ~ 5 secondes.
Shan Dou
shell=Trueest mauvais pour la sécurité, il vaut mieux l'éviter.
Alexey Vazhnov
Fair point, édité
1 ''
15

J'utiliserais la méthode d'objet fichier de Python readlines, comme suit:

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

Cela ouvre le fichier, crée une liste de lignes dans le fichier, compte la longueur de la liste, l'enregistre dans une variable et ferme à nouveau le fichier.

Daniel Lee
la source
6
Bien que ce soit l'un des premiers moyens qui vient à l'esprit, il n'est probablement pas très efficace en mémoire, surtout si l'on compte les lignes dans des fichiers jusqu'à 10 Go (comme je le fais), ce qui est un inconvénient notable.
Steen Schütt
@TimeSheep Est-ce un problème pour les fichiers avec beaucoup (par exemple, des milliards) de petites lignes, ou les fichiers qui ont des lignes extrêmement longues (par exemple, Gigaoctets par ligne)?
robert
La raison pour laquelle je demande est, il semblerait que le compilateur devrait être en mesure d'optimiser cela en ne créant pas de liste intermédiaire.
robert
@dmityugov Per Python docs, xreadlinesest obsolète depuis 2.3, car il ne fait que renvoyer un itérateur. for line in fileest le remplacement indiqué. Voir: 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
la source
12

Voici ce que j'utilise, semble assez propre:

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

MISE À JOUR: C'est légèrement plus rapide que l'utilisation de python pur, mais au détriment de l'utilisation de la mémoire. Le sous-processus va générer un nouveau processus avec la même empreinte mémoire que le processus parent pendant qu'il exécute votre commande.

radtek
la source
1
Juste comme remarque, cela ne fonctionnera pas sur Windows bien sûr.
Bram Vanroy
les principaux utilitaires fournissent apparemment "wc" pour windows stackoverflow.com/questions/247234/… . Vous pouvez également utiliser une machine virtuelle Linux dans votre boîte Windows si votre code finira par fonctionner sous Linux dans Prod.
radtek
Ou WSL, fortement conseillé sur n'importe quelle VM si des trucs comme celui-ci sont la seule chose que vous faites. :-)
Bram Vanroy
Ouais ça marche. Je ne suis pas un gars de Windows, mais de goolging, j'ai appris WSL = Windows Subsystem for Linux =)
radtek
3
python3.7: octets de retour de sous-processus, donc le code ressemble à ceci: int (subprocess.check_output (['wc', '-l', file_path]). decode ("utf-8"). lstrip (). split (" ") [0])
Alexey Alexeenka
11

C'est la chose la plus rapide que j'ai trouvée en utilisant du python pur. Vous pouvez utiliser la quantité de mémoire que vous souhaitez en définissant un tampon, bien que 2 ** 16 semble être un point idéal sur mon ordinateur.

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

J'ai trouvé la réponse ici Pourquoi la lecture des lignes de stdin est-elle beaucoup plus lente en C ++ qu'en Python? et l'ajusté juste un tout petit peu. C'est une très bonne lecture pour comprendre comment compter les lignes rapidement, bien qu'il wc -lsoit toujours environ 75% plus rapide qu'autre chose.

jeffpkamp
la source
9

J'ai obtenu une petite amélioration (4-8%) avec cette version qui réutilise un tampon constant afin d'éviter toute surcharge mémoire ou GC:

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

Vous pouvez jouer avec la taille du tampon et peut-être voir une petite amélioration.

Scott Persinger
la source
Agréable. Pour tenir compte des fichiers qui ne se terminent pas par \ n, ajoutez 1 en dehors de la boucle si le tampon et le tampon [-1]! = '\ N'
ryuusenshi
Un bug: le tampon du dernier tour peut ne pas être propre.
Jay
que se passe-t-il si entre les tampons, une partie se termine par \ et l'autre partie commence par n? qui manquera une nouvelle ligne là-dedans, je fusionnerais avec des variables pour stocker la fin et le début de chaque morceau, mais cela pourrait ajouter plus de temps au script = (
pelos
9

La réponse de Kyle

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

est probablement le meilleur, une alternative pour cela est

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

Voici la comparaison des performances des deux

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
la source
9

Solution en une ligne:

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

Mon extrait:

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

0 bar.txt
1000 command.txt
3 test_file.txt
1003 total
L'Exorciste
la source
Bonne idée, malheureusement, cela ne fonctionne pas sur Windows.
Kim
3
si vous voulez être surfeur de python, dites au revoir à windows. Croyez-moi vous me remercierez un jour.
TheExorcist
6
Je viens de noter qu'il convient de noter que cela ne fonctionnera que sur les fenêtres. Je préfère travailler moi-même sur une pile Linux / Unix, mais lors de l'écriture d'un logiciel à mon humble avis, il faut tenir compte des effets secondaires qu'un programme pourrait avoir lorsqu'il est exécuté sous différents systèmes d'exploitation. Comme l'OP n'a pas mentionné sa plate-forme et au cas où quelqu'un viendrait sur cette solution via google et la copierait (ignorant les limitations qu'un système Windows pourrait avoir), je voulais ajouter la note.
Kim
Vous ne pouvez pas enregistrer la sortie de os.system()dans une variable et la post-traiter de toute façon.
Un Se
@AnSe vous avez raison mais la question n'est pas posée de savoir si elle enregistre ou non. Je suppose que vous comprenez le contexte.
TheExorcist
6

Juste pour compléter les méthodes ci-dessus, j'ai essayé une variante avec le module fileinput:

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

Et passé un fichier de lignes de 60mil à toutes les méthodes indiquées ci-dessus:

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

C'est une petite surprise pour moi que l'entrée de fichier soit si mauvaise et évolue bien pire que toutes les autres méthodes ...

BandGap
la source
5

Quant à moi, cette variante sera la plus rapide:

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

raisons: tampon plus rapide que la lecture ligne par ligne et string.countest également très rapide

Mykola Kharechko
la source
1
Mais est-ce? Au moins sur OSX / python2.5, la version de l'OP est encore environ 10% plus rapide selon timeit.py.
dF.
Que faire si la dernière ligne ne se termine pas par '\ n'?
tzot
1
Je ne sais pas comment vous l'avez testé, dF, mais sur ma machine, c'est ~ 2,5 fois plus lent que toute autre option.
SilentGhost
34
Vous déclarez que ce sera le plus rapide, puis déclarez que vous ne l'avez pas testé. Pas très scientifique hein? :)
Ólafur Waage
Voir la solution et les statistiques fournies par la réponse de Ryan Ginstrom ci-dessous. Consultez également le commentaire de JF Sebastian et le lien sur la même réponse.
SherylHohman
5

Ce code est plus court et plus clair. C'est probablement le meilleur moyen:

num_lines = open('yourfile.ext').read().count('\n')
Texom512
la source
6
Vous devez également fermer le fichier.
rsm
6
Il chargera tout le fichier en mémoire.
Ivelin
pas mieux quand on a besoin de performances sur des fichiers volumineux
mabraham
4

J'ai modifié le boîtier tampon comme ceci:

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

Désormais, les fichiers vides et la dernière ligne (sans \ n) sont également comptés.

Mannequin
la source
Peut-être aussi expliquer (ou ajouter un commentaire dans le code) ce que vous avez changé et pourquoi;). Pourrait donner aux gens un peu plus à l'intérieur de votre code beaucoup plus facilement (plutôt que "l'analyse" du code dans le cerveau).
Styxxy
L'optimisation de boucle, je pense, permet à Python de faire une recherche de variable locale sur read_f, python.org/doc/essays/list2str
The Red Pea
3

Et ça

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

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

pyanon
la source
Cela donne le nombre -1 de la vraie valeur.
Borealis
Le deuxième argument facultatif pour enumerate()est le nombre de début selon docs.python.org/2/library/functions.html#enumerate
MarkHu
3
print open('file.txt', 'r').read().count("\n") + 1
Andrés Torres
la source
3
def line_count(path):
    count = 0
    with open(path) as lines:
        for count, l in enumerate(lines, start=1):
            pass
    return count
mdwhatcott
la source
3

Si l'on veut obtenir le nombre de lignes à moindre coût en Python sous Linux, je recommande cette méthode:

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

file_path peut être à la fois un chemin de fichier abstrait ou un chemin relatif. J'espère que cela peut vous aider.

Lerner Zhang
la source
2

Que dis-tu de ça?

import fileinput
import sys

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

fileinput.close()
print counter
leba-lev
la source
2

Que diriez-vous de ce one-liner:

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

Prend 0,003 s en utilisant cette méthode pour le chronométrer sur un fichier de 3900 lignes

def c():
  import time
  s = time.time()
  file_length = len(open('myfile.txt','r').read().split('\n'))
  print time.time() - s
onetwopunch
la source
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
la source
Pourriez-vous expliquer ce qui ne va pas si vous pensez que c'est mal? Ça a marché pour moi. Merci!
jciloa
J'aimerais savoir pourquoi cette réponse a également été rejetée. Il parcourt le fichier par lignes et les résume. Je l'aime, c'est court et au point, qu'est-ce qui ne va pas?
cessor
2

Méthode simple:

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
la source
3
Dans cet exemple, le fichier n'est pas fermé.
Maciej M
9
OP voulait quelque chose de mémoire efficace. Ce n'est certainement pas ça.
Andy Carlson
1

le résultat de l'ouverture d'un fichier est un itérateur, qui peut être converti en une séquence, qui a une longueur:

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

c'est plus concis que votre boucle explicite et évite le enumerate.

Andrew Jaffe
la source
10
ce qui signifie que le fichier de 100 Mo devra être lu dans la mémoire.
SilentGhost
oui, bon point, même si je m'interroge sur la différence de vitesse (par opposition à la mémoire). Il est probablement possible de créer un itérateur qui fait cela, mais je pense que ce serait équivalent à votre solution.
Andrew Jaffe
6
-1, ce n'est pas seulement la mémoire, mais avoir à construire la liste en mémoire.
orip
0

Vous pouvez utiliser le os.pathmodule de la manière suivante:

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

, où Filenameest le chemin absolu du fichier.

Victor
la source
1
Qu'est-ce que cette réponse a à voir avec os.path?
moi
0

Si le fichier peut tenir en mémoire, alors

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