2009-01-30 5 views

Répondre

39

La méthode que vous avez affichée n'est pas particulièrement bonne. Laisse briser cette part:

// new StreamReader("file.txt").ReadToEnd().Split(new char[] {'\n'}).Length 
//  becomes this: 
var file = new StreamReader("file.txt").ReadToEnd(); // big string 
var lines = file.Split(new char[] {'\n'});   // big array 
var count = lines.Count; 

Vous tenez réellement ce fichier en mémoire deux fois: une fois pour lire toutes les lignes, une fois à scinder en un tableau. Le garbage collector déteste ça.

Si vous aimez les liners, vous pouvez écrire System.IO.File.ReadAllLines(filePath).Length, mais cela récupère tout le fichier dans un tableau. Cela ne sert à rien de le faire si vous ne tenez pas au tableau.

Une solution plus rapide serait:

int TotalLines(string filePath) 
{ 
    using (StreamReader r = new StreamReader(filePath)) 
    { 
     int i = 0; 
     while (r.ReadLine() != null) { i++; } 
     return i; 
    } 
} 

Le code contient ci-dessus (au plus) une ligne de texte en mémoire à un moment donné. Il va être efficace tant que les lignes sont relativement courtes.

6

Eh bien, le problème avec cela est que vous allouez beaucoup de la mémoire lors de cette opération sur des fichiers volumineux. Je préfère lire le fichier ligne par ligne et incrémenter manuellement un compteur. Ce n'est peut-être pas un one-liner mais c'est beaucoup plus efficace sur le plan de la mémoire.

Vous pouvez également charger les données dans des blocs de taille égale et compter les sauts de ligne dans celles-ci. C'est probablement le moyen le plus rapide.

2

Bien sûr, il lit le flux entier en mémoire. C'est laconique, mais je peux créer un fichier aujourd'hui qui va échouer si fort.

Lit un caractère à la fois et incrémente votre compte sur le retour à la ligne.

EDIT - après quelques recherches rapides Si vous voulez laconique et que vous voulez que brillante nouvelle sensation générique, considérez ceci:

public class StreamEnumerator : IEnumerable<char> 
{ 
    StreamReader _reader; 

    public StreamEnumerator(Stream stm) 
    { 
     if (stm == null) 
      throw new ArgumentNullException("stm"); 
     if (!stm.CanSeek) 
      throw new ArgumentException("stream must be seekable", "stm"); 
     if (!stm.CanRead) 
      throw new ArgumentException("stream must be readable", "stm"); 

     _reader = new StreamReader(stm); 
    } 

    public IEnumerator<char> GetEnumerator() 
    { 
     int c = 0; 
     while ((c = _reader.Read()) >= 0) 
     { 
      yield return (char)c; 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

qui définit une nouvelle classe qui vous permet d'énumérer sur les flux, puis votre code de comptage peut ressembler à ceci:

StreamEnumerator chars = new StreamEnumerator(stm); 
int lines = chars.Count(c => c == '\n'); 

qui vous donne une belle expression laconique lambda à faire (plus ou moins) ce que vous voulez.

Je préfère encore le Old Skool:

public static int CountLines(Stream stm) 
    { 
     StreamReader _reader = new StreamReader(stm); 
     int c = 0, count = 0; 
     while ((c = _reader.Read()) != -1) 
     { 
      if (c == '\n') 
      { 
       count++; 
      } 
     } 
     return count; 
    } 

NB: version Environment.NewLine gauche comme un exercice pour le lecteur

+0

Cela ne fonctionnerait pas lors de la recherche Environment.NewLine , qui est généralement une chaîne de deux caractères (CrLf). – spoulson

+0

Il a cependant la bonne idée. Alors, que diriez-vous d'utiliser un RegEx pour rechercher des instances de Environment.NewLine? – JMD

-1

En supposant que le fichier existe et vous pouvez l'ouvrir, cela fonctionnera.

Il est peu lisible ou en toute sécurité ...

4

Si vous êtes à la recherche d'une solution à court, je peux au moins vous permet d'économiser vous donner une seule ligne d'avoir à diviser le résultat:

int i = File.ReadAllLines("file.txt").Count; 

Mais cela pose les mêmes problèmes de lecture d'un gros fichier en mémoire que votre original. Vous devriez vraiment utiliser un lecteur de flux et compter les sauts de ligne pendant que vous les lisez jusqu'à ce que vous atteigniez la fin du fichier.

+0

Oublié de mettre les parenthèses: int i = File.ReadAllLines ("file.txt"). Count(); –

0

Mayby this?

string file = new StreamReader("YourFile.txt").ReadToEnd(); 
string[] lines = file.Split('\n'); 
int countOfLines = lines.GetLength(0)); 
Questions connexes