Risposte:
Modifica seriamente tardiva: se si utilizza .NET 4.0 o versioni successive
La File
classe ha un nuovo ReadLines
metodo 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.
ReadLines().Count()
ciò dovrai aggiungere un using System.Linq
alle 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.
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.
Potresti leggerlo rapidamente e incrementare un contatore, basta usare un ciclo per incrementare, senza fare nulla con il testo.
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.
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.
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);
}
È 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).