61 votes

Compter le nombre de lignes d'un fichier sans lire tout le fichier en mémoire ?

Je traite d'énormes fichiers de données (des millions de lignes chacun).

Avant de commencer le traitement, j'aimerais obtenir un compte du nombre de lignes dans le fichier, afin de pouvoir ensuite indiquer où en est le traitement.

En raison de la taille des fichiers, il ne serait pas pratique de lire le fichier entier en mémoire, juste pour compter le nombre de lignes. Quelqu'un a-t-il une idée de la façon de procéder ?

79voto

glenn jackman Points 69748

Lire le fichier une ligne à la fois :

count = File.foreach(filename).inject(0) {|c, line| c+1}

ou le Perl-ish

File.foreach(filename) {}
count = $.

ou

count = 0
File.open(filename) {|f| count = f.read.count("\n")}

sera plus lent que

count = %x{wc -l #{filename}}.split.first.to_i

7 votes

La dernière est la plus propre, puisque nous pouvons supposer que "wc" est optimisé pour de bonnes vitesses d'entrée/sortie. Les ".split.first" sont superflus, et n'oubliez pas d'ajouter des guillemets simples autour du nom de fichier, ou cela échouera sur les noms de fichiers qui ont des espaces. Simplifié : %x{wc -l '#{nomdufichier}'}.to_i

4 votes

Ou count = %x{wc -l < "#{filename}"}.to_i

4 votes

@deafgreatdane Je ne pense pas que wc est "propre". Maintenant, il ne fonctionnera pas sous Windows J'accepterais une légère baisse de performance (dans la même classe de complexité) pour éviter les problèmes de portabilité.

74voto

DJ. Points 2663

Si vous êtes dans un environnement Unix, vous pouvez simplement laisser wc -l faire le travail.

Il ne chargera pas l'intégralité du fichier en mémoire ; étant donné qu'il est optimisé pour le streaming de fichiers et le comptage de mots/lignes, les performances sont suffisantes pour remplacer le streaming du fichier en Ruby.

3 votes

Le WC est si rapide que l'on n'aura probablement pas besoin d'un compteur de progression.

9 votes

Il y a une condition marginale : si la dernière ligne du fichier ne comporte pas de nouvelle ligne, il manque une ligne à wc. Ceci est prévu par Posix, voir backreference.org/2010/05/23/

5 votes

S'il vous plaît, faites plus que citer la méthode, citez un exemple de wc -l en pratique. tout le monde ne connaît pas les choses qui sont évidentes pour vous. (Je sais, "Google plus fort !"... mais si nous pouvions tous être plus comme Ruby, nous ferions cela instinctivement).

15voto

cletus Points 276888

Quel que soit le langage que vous utilisez, vous allez devoir lire l'ensemble du fichier si les lignes sont de longueur variable. En effet, les nouvelles lignes peuvent se trouver n'importe où et il n'y a aucun moyen de le savoir sans lire le fichier (en supposant qu'il ne soit pas mis en cache, ce qui n'est généralement pas le cas).

Si vous voulez indiquer des progrès, vous avez deux options réalistes. Vous pouvez extrapoler la progression en fonction de la longueur présumée de la ligne :

assumed lines in file = size of file / assumed line size
progress = lines processed / assumed lines in file * 100%

puisque vous connaissez la taille du fichier. Alternativement, vous pouvez mesurer la progression comme :

progress = bytes processed / size of file * 100%

Cela devrait être suffisant.

0 votes

En fait, pour mes besoins, c'est probablement une meilleure idée que de compter le nombre de lignes.

2 votes

Je suppose que le posteur d'origine était d'accord pour lire le fichier, mais pas pour avoir tout son contenu en mémoire.

13voto

JBoy Points 897

en utilisant Ruby :

file=File.open("path-to-file","r")
file.readlines.size

39 millisecondes plus rapide que wc -l sur un fichier de 325.477 lignes

1 votes

Le système de votre wc a des problèmes.

1 votes

Je suis certain que vous ne pouvez pas faire cela avec un fichier énorme, car son contenu sera entièrement conservé en mémoire.

0 votes

Utilisation de readlines o read n'est pas extensible. C'est aussi plus lent que de lire ligne par ligne en utilisant foreach dès que la taille des fichiers dépasse 1 Mo. Voir stackoverflow.com/q/25189262/128421

2voto

fbonetti Points 1731

Pour des raisons que je ne comprends pas bien, la recherche de nouvelles lignes dans le fichier à l'aide de la fonction File semble être beaucoup plus rapide que de faire CSV#readlines.count .

Le benchmark suivant utilise un fichier CSV contenant 1 045 574 lignes de données et 4 colonnes :

       user     system      total        real
   0.639000   0.047000   0.686000 (  0.682000)
  17.067000   0.171000  17.238000 ( 17.221173)

Le code pour le benchmark est ci-dessous :

require 'benchmark'
require 'csv'

file = "1-25-2013 DATA.csv"

Benchmark.bm do |x|
    x.report { File.read(file).scan(/\n/).count }
    x.report { CSV.open(file, "r").readlines.count }
end

Comme vous pouvez le constater, la recherche de nouvelles lignes dans le fichier est un ordre de grandeur plus rapide.

0 votes

Le scan force in-memory et l'objet fichier est fui.

0 votes

Pour un fichier de 10M de lignes, je vois IOError : Fichier trop volumineux.

1 votes

File.read le charge et ne lui fait rien. CSV.open(...).readlines lit et analyse chaque ligne dans des tableaux. Vous comparez des pommes avec des oranges, donc, bien sûr, il y aura une grande différence de vitesse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X