Determina il numero di righe all'interno di un file di testo


209

Esiste un modo semplice per determinare a livello di codice il numero di righe all'interno di un file di testo?

Risposte:


396

Modifica seriamente tardiva: se si utilizza .NET 4.0 o versioni successive

La Fileclasse ha un nuovo ReadLinesmetodo che elenca pigramente le righe piuttosto che leggerle avidamente tutte in una matrice come ReadAllLines. Quindi ora puoi avere sia efficienza che concisione con:

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

Risposta originale

Se non ti preoccupi troppo dell'efficienza, puoi semplicemente scrivere:

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

Per un metodo più efficiente potresti fare:

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

Modifica: in risposta a domande sull'efficienza

Il motivo per cui ho detto che il secondo era più efficiente riguardava l'utilizzo della memoria, non necessariamente la velocità. Il primo carica l'intero contenuto del file in un array, il che significa che deve allocare almeno la quantità di memoria della dimensione del file. Il secondo esegue semplicemente il loop di una riga alla volta, quindi non deve mai allocare più di una riga di memoria alla volta. Questo non è così importante per i file di piccole dimensioni, ma per i file più grandi potrebbe essere un problema (se si tenta di trovare il numero di righe in un file da 4 GB su un sistema a 32 bit, ad esempio, dove semplicemente non è sufficiente spazio degli indirizzi in modalità utente per allocare un array così grande).

In termini di velocità, non mi aspetto che ci sia molto da fare. È possibile che ReadAllLines abbia alcune ottimizzazioni interne, ma d'altra parte potrebbe dover allocare un grosso pezzo di memoria. Immagino che ReadAllLines potrebbe essere più veloce per file di piccole dimensioni, ma significativamente più lento per file di grandi dimensioni; sebbene l'unico modo per dirlo sarebbe misurarlo con un cronometro o un profiler di codice.


2
Piccola nota: poiché String è un tipo di riferimento, la matrice sarebbe la dimensione del numero di righe x la dimensione di un puntatore, ma è corretto che sia ancora necessario memorizzare il testo, ogni riga come un singolo oggetto String.
Mike Dimmick,

16
Cordiali saluti: Per fare ReadLines().Count()ciò dovrai aggiungere un using System.Linqalle tue inclusioni. Sembrava abbastanza non intuitivo richiedere quell'aggiunta, quindi è per questo che lo menziono. Se stai utilizzando Visual Studio è probabile che questa aggiunta venga eseguita automaticamente.
Nucleon,

2
Ho testato entrambi gli approcci, "File.ReadLines.Count ()" v / s "reader.ReadLine ()" e "reader.ReadLine ()" è leggermente più veloce ma è più veloce con un margine molto piccolo. "ReadAllLines" è più flessibile che richiede il doppio del tempo e consuma molta memoria). Questo perché "File.ReadLines.Count ()" e "reader.ReadLine ()" è un enumeratore che legge il file riga per riga e non carica l'intero file in memoria per leggerlo di nuovo nella RAM.
Yogee,

9
Sì, nessuno lavora mai con file da 4 GB +. Certamente non abbiamo mai a che fare con file di registro così grandi. Oh aspetta.
Greg Beech,

2
Se vuoi vedere l'interno di File.ReadLines () vai qui: System.IO.File.cs Quando esegui il drill down attraverso i sovraccarichi ti porta qui: ReadLinesIterator.cs
Steve Kinyon,

12

Il più facile:

int lines = File.ReadAllLines("myfile").Length;

8

Ciò richiederebbe meno memoria, ma probabilmente richiederà più tempo

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

5

Se per facile intendi una riga di codice facile da decifrare ma per caso inefficiente?

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

Questo è probabilmente il modo più veloce per sapere quante righe.

Potresti anche farlo (a seconda se lo stai inserendo nel buffer)

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

Ci sono altri numerosi modi, ma uno dei precedenti è probabilmente quello con cui andrai.


3
Sostengo che questo metodo è molto inefficiente; perché stai leggendo l'intero file in memoria e in un array di stringhe, nientemeno. Non è necessario copiare il buffer quando si utilizza ReadLine. Vedi la risposta di @GregBeech. Mi dispiace piovere sulla tua sfilata.
Mike Christian,

2

Potresti leggerlo rapidamente e incrementare un contatore, basta usare un ciclo per incrementare, senza fare nulla con il testo.


3
Questo dovrebbe essere un commento, non una risposta.
IamBatman

2

La lettura di un file in sé e per sé richiede del tempo, la garbage collection del risultato è un altro problema mentre leggi l'intero file solo per contare i caratteri di nuova riga,

Ad un certo punto, qualcuno dovrà leggere i caratteri nel file, indipendentemente dal fatto che si tratti del framework o del codice. Ciò significa che è necessario aprire il file e leggerlo in memoria se il file è grande, questo potrebbe essere potenzialmente un problema poiché la memoria deve essere raccolta in modo inutile.

Nima Ara ha fatto una bella analisi che potresti prendere in considerazione

Ecco la soluzione proposta, in quanto legge 4 caratteri alla volta, conta il carattere di avanzamento riga e riutilizza nuovamente lo stesso indirizzo di memoria per il successivo confronto di caratteri.

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;
}

Sopra puoi vedere che una riga viene letta un carattere alla volta anche dal framework sottostante in quanto devi leggere tutti i caratteri per vedere il feed della riga.

Se lo profilassi come Nima fatto, vedresti che questo è un modo piuttosto veloce ed efficiente per farlo.


1

contare i ritorni a capo / avanzamenti riga. Credo che nell'unicode siano ancora rispettivamente 0x000D e 0x000A. in questo modo puoi essere efficiente o inefficiente quanto vuoi e decidere se devi affrontare entrambi i personaggi o meno


1

Un'opzione praticabile, che ho usato personalmente, sarebbe quella di aggiungere la tua intestazione alla prima riga del file. L'ho fatto per un formato modello personalizzato per il mio gioco. Fondamentalmente, ho uno strumento che ottimizza i miei file .obj, eliminando la merda che non mi serve, li converte in un layout migliore e quindi scrive il numero totale di linee, facce, normali, vertici e texture UV la prima riga. Tali dati vengono quindi utilizzati da vari buffer di array quando viene caricato il modello.

Ciò è utile anche perché è necessario eseguire il ciclo del file solo una volta per caricarlo, anziché una volta per contare le righe e leggere nuovamente i dati nei buffer creati.


-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);
}         

5
-1: questo sarà LENTO, consumerà molta memoria e darà del tempo a GC!
ya23,

-2

È possibile avviare l' eseguibile " wc .exe" (fornito con UnixUtils e non necessita di installazione) eseguito come processo esterno. Supporta diversi metodi di conteggio delle righe (come unix vs mac vs windows).


1
Non c'è modo che questo sia abbastanza veloce per essere utile. Il sovraccarico di chiamare semplicemente l'eseguibile sarebbe il doppio (l'ovvia esagerazione è ovvia) di un singolo ciclo incrementale.
Krythic,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.