Déterminer le nombre de lignes dans un fichier texte

209

Existe-t-il un moyen simple de déterminer par programme le nombre de lignes dans un fichier texte?

TK.
la source

Réponses:

396

Édition sérieusement tardive: si vous utilisez .NET 4.0 ou version ultérieure

La Fileclasse a une nouvelle ReadLinesméthode qui énumère paresseusement les lignes plutôt que de les lire avidement dans un tableau comme ReadAllLines. Alors maintenant, vous pouvez avoir à la fois efficacité et concision avec:

var lineCount = File.ReadLines(@"C:\file.txt").Count();

Réponse originale

Si vous ne vous souciez pas trop de l'efficacité, vous pouvez simplement écrire:

var lineCount = File.ReadAllLines(@"C:\file.txt").Length;

Pour une méthode plus efficace, vous pouvez faire:

var lineCount = 0;
using (var reader = File.OpenText(@"C:\file.txt"))
{
    while (reader.ReadLine() != null)
    {
        lineCount++;
    }
}

Edit: En réponse aux questions sur l'efficacité

La raison pour laquelle j'ai dit que la seconde était plus efficace concernait l'utilisation de la mémoire, pas nécessairement la vitesse. Le premier charge tout le contenu du fichier dans un tableau, ce qui signifie qu'il doit allouer au moins autant de mémoire que la taille du fichier. La seconde boucle simplement une ligne à la fois, de sorte qu'elle n'a jamais à allouer plus d'une valeur de mémoire à la fois. Ce n'est pas si important pour les petits fichiers, mais pour les fichiers plus gros, cela pourrait être un problème (si vous essayez de trouver le nombre de lignes dans un fichier de 4 Go sur un système 32 bits, par exemple, où il n'y en a tout simplement pas assez) espace d'adressage en mode utilisateur pour allouer un tableau de cette taille).

En termes de vitesse, je ne m'attendrais pas à ce qu'il y en ait beaucoup. Il est possible que ReadAllLines ait quelques optimisations internes, mais d'un autre côté, il devra peut-être allouer une énorme quantité de mémoire. Je suppose que ReadAllLines pourrait être plus rapide pour les petits fichiers, mais beaucoup plus lent pour les gros fichiers; bien que la seule façon de le savoir soit de le mesurer avec un chronomètre ou un profileur de code.

Greg Beech
la source
2
Petite note: parce que String est un type de référence, le tableau aurait la taille du nombre de lignes x la taille d'un pointeur, mais vous avez raison de dire qu'il doit toujours stocker le texte, chaque ligne comme un seul objet String.
Mike Dimmick
16
FYI: Pour ce faire, ReadLines().Count()vous devrez ajouter un using System.Linqà vos inclusions. Il semblait assez peu intuitif d'exiger cet ajout, c'est pourquoi je le mentionne. Si vous utilisez Visual Studio, cet ajout est probablement effectué automatiquement pour vous.
Nucleon
2
J'ai testé les deux approches, "File.ReadLines.Count ()" v / s "reader.ReadLine ()" et "reader.ReadLine ()" est légèrement plus rapide mais plus rapide par très peu de marge. "ReadAllLines" est plus lâche ce qui prend le double du temps et mange beaucoup de mémoire). En effet, "File.ReadLines.Count ()" et "reader.ReadLine ()" est un énumérateur qui lit le fichier ligne par ligne et ne charge pas le fichier entier en mémoire, le relit en RAM.
Yogee
9
Oui, personne ne travaille jamais avec des fichiers 4 Go +. Nous n'avons certainement jamais affaire à des fichiers journaux aussi volumineux. Oh, attendez.
Greg Beech
2
Si vous voulez voir l'intérieur de File.ReadLines () allez ici: System.IO.File.cs Lorsque vous explorez
Steve Kinyon
12

Le plus facile:

int lines = File.ReadAllLines("myfile").Length;
leppie
la source
8

Cela utiliserait moins de mémoire, mais prendrait probablement plus de temps

int count = 0;
string line;
TextReader reader = new StreamReader("file.txt");
while ((line = reader.ReadLine()) != null)
{
  count++;
}
reader.Close();
benPearce
la source
5

Si par facilité, vous entendez des lignes de code faciles à déchiffrer mais par chance inefficaces?

string[] lines = System.IO.File.RealAllLines($filename);
int cnt = lines.Count();

C'est probablement le moyen le plus rapide de savoir combien de lignes.

Vous pouvez également le faire (selon que vous le mettez en mémoire tampon)

#for large files
while (...reads into buffer){
string[] lines = Regex.Split(buffer,System.Enviorment.NewLine);
}

Il existe de nombreuses autres façons, mais l'une des options ci-dessus est probablement celle avec laquelle vous irez.

user8456
la source
3
Je soutiens que cette méthode est très inefficace; parce que vous lisez le fichier entier en mémoire et dans un tableau de chaînes, rien de moins. Vous n'avez pas à copier le tampon lorsque vous utilisez ReadLine. Voir la réponse de @GregBeech. Excuse-moi de gâcher ton défilé.
Mike Christian
2

Vous pouvez le lire rapidement et incrémenter un compteur, utilisez simplement une boucle pour incrémenter, sans rien faire avec le texte.

Vendeurs Mitchel
la source
3
Cela devrait être un commentaire, pas une réponse.
IamBatman
2

La lecture d'un fichier en lui-même prend un certain temps, la récupération du résultat est un autre problème lorsque vous lisez le fichier entier juste pour compter le (s) caractère (s) de nouvelle ligne,

À un moment donné, quelqu'un devra lire les caractères du fichier, que ce soit le framework ou si c'est votre code. Cela signifie que vous devez ouvrir le fichier et le lire dans la mémoire si le fichier est volumineux, cela va potentiellement être un problème car la mémoire doit être récupérée.

Nima Ara a fait une belle analyse que vous pourriez prendre en considération

Voici la solution proposée, car il lit 4 caractères à la fois, compte le caractère de saut de ligne et réutilise la même adresse mémoire pour la prochaine comparaison de caractères.

private const char CR = '\r';  
private const char LF = '\n';  
private const char NULL = (char)0;

public static long CountLinesMaybe(Stream stream)  
{
    Ensure.NotNull(stream, nameof(stream));

    var lineCount = 0L;

    var byteBuffer = new byte[1024 * 1024];
    const int BytesAtTheTime = 4;
    var detectedEOL = NULL;
    var currentChar = NULL;

    int bytesRead;
    while ((bytesRead = stream.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
    {
        var i = 0;
        for (; i <= bytesRead - BytesAtTheTime; i += BytesAtTheTime)
        {
            currentChar = (char)byteBuffer[i];

            if (detectedEOL != NULL)
            {
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 1];
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 2];
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 3];
                if (currentChar == detectedEOL) { lineCount++; }
            }
            else
            {
                if (currentChar == LF || currentChar == CR)
                {
                    detectedEOL = currentChar;
                    lineCount++;
                }
                i -= BytesAtTheTime - 1;
            }
        }

        for (; i < bytesRead; i++)
        {
            currentChar = (char)byteBuffer[i];

            if (detectedEOL != NULL)
            {
                if (currentChar == detectedEOL) { lineCount++; }
            }
            else
            {
                if (currentChar == LF || currentChar == CR)
                {
                    detectedEOL = currentChar;
                    lineCount++;
                }
            }
        }
    }

    if (currentChar != LF && currentChar != CR && currentChar != NULL)
    {
        lineCount++;
    }
    return lineCount;
}

Ci-dessus, vous pouvez voir qu'une ligne est lue un caractère à la fois par le cadre sous-jacent, car vous devez lire tous les caractères pour voir le saut de ligne.

Si vous le profilez comme fait baie Nima, vous verriez que c'est un moyen assez rapide et efficace de le faire.

Walter Vehoeven
la source
1

compter les retours chariot / sauts de ligne. Je crois qu'en Unicode, ils sont toujours respectivement 0x000D et 0x000A. de cette façon, vous pouvez être aussi efficace ou inefficace que vous le souhaitez, et décider si vous devez traiter avec les deux personnages ou non

géocoin
la source
1

Une option viable, et que j'ai personnellement utilisée, serait d'ajouter votre propre en-tête à la première ligne du fichier. J'ai fait cela pour un format de modèle personnalisé pour mon jeu. Fondamentalement, j'ai un outil qui optimise mes fichiers .obj, se débarrasse de la merde dont je n'ai pas besoin, les convertit en une meilleure mise en page, puis écrit le nombre total de lignes, faces, normales, sommets et UV de texture sur la toute première ligne. Ces données sont ensuite utilisées par divers tampons de tableau lorsque le modèle est chargé.

Ceci est également utile car vous n'avez besoin de parcourir le fichier qu'une seule fois pour le charger, au lieu d'une fois pour compter les lignes, et encore pour lire les données dans vos tampons créés.

Krythic
la source
-1
try {
    string path = args[0];
    FileStream fh = new FileStream(path, FileMode.Open, FileAccess.Read);
    int i;
    string s = "";
    while ((i = fh.ReadByte()) != -1)
        s = s + (char)i;

    //its for reading number of paragraphs
    int count = 0;
    for (int j = 0; j < s.Length - 1; j++) {
            if (s.Substring(j, 1) == "\n")
                count++;
    }

    Console.WriteLine("The total searches were :" + count);

    fh.Close();

} catch(Exception ex) {
    Console.WriteLine(ex.Message);
}         
Muhammad Usman -kai hiwatari
la source
5
-1: ce sera LENT, consommera beaucoup de mémoire et donnera du mal au GC!
ya23
-2

Vous pouvez lancer l' exécutable " wc .exe" (livré avec UnixUtils et n'a pas besoin d'installation) exécuté en tant que processus externe. Il prend en charge différentes méthodes de comptage de lignes (comme unix vs mac vs windows).

Sklivvz
la source
1
Il n'y a aucun moyen que ce soit assez rapide pour être utile. Le surcoût de simplement appeler l'exécutable serait deux fois plus (une exagération évidente est évidente) qu'une seule boucle d'incrémentation.
Krythic