Determinar el número de líneas dentro de un archivo de texto

209

¿Hay una manera fácil de determinar mediante programación el número de líneas dentro de un archivo de texto?

TK
fuente

Respuestas:

396

Edición muy tardía: si está utilizando .NET 4.0 o posterior

La Fileclase tiene un nuevo ReadLinesmétodo que enumera perezosamente las líneas en lugar de leerlas con avidez en una matriz como ReadAllLines. Entonces ahora puede tener eficiencia y concisión con:

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

Respuesta original

Si no le preocupa demasiado la eficiencia, simplemente puede escribir:

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

Para un método más eficiente que podría hacer:

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

Editar: en respuesta a preguntas sobre eficiencia

La razón por la que dije que el segundo era más eficiente era el uso de memoria, no necesariamente la velocidad. El primero carga todo el contenido del archivo en una matriz, lo que significa que debe asignar al menos tanta memoria como el tamaño del archivo. El segundo simplemente recorre una línea a la vez, por lo que nunca tiene que asignar más de una línea de memoria a la vez. Esto no es tan importante para archivos pequeños, pero para archivos más grandes podría ser un problema (si intenta encontrar el número de líneas en un archivo de 4GB en un sistema de 32 bits, por ejemplo, donde simplemente no hay suficiente espacio de direcciones en modo de usuario para asignar una matriz de este tamaño).

En términos de velocidad, no esperaría que hubiera mucho. Es posible que ReadAllLines tenga algunas optimizaciones internas, pero por otro lado puede que tenga que asignar una gran cantidad de memoria. Supongo que ReadAllLines podría ser más rápido para archivos pequeños, pero significativamente más lento para archivos grandes; aunque la única forma de saberlo sería medirlo con un cronómetro o un generador de perfiles de código.

Greg Beech
fuente
2
Pequeña nota: dado que String es un tipo de referencia, la matriz tendría el tamaño del número de líneas x el tamaño de un puntero, pero tiene razón en que todavía necesita almacenar el texto, cada línea como un solo objeto String.
Mike Dimmick
16
FYI: Para hacerlo ReadLines().Count(), deberá agregar un using System.Linqa sus inclusiones. Parecía bastante no intuitivo requerir esa adición, por eso lo menciono. Si está utilizando Visual Studio, es probable que esta adición se realice automáticamente.
Nucleon
2
He probado ambos enfoques, "File.ReadLines.Count ()" v / s "reader.ReadLine ()" y "reader.ReadLine ()" es un poco más rápido pero es más rápido por muy poco margen. "ReadAllLines" es más flexible, toma el doble de tiempo y consume mucha memoria). Esto se debe a que "File.ReadLines.Count ()" y "reader.ReadLine ()" es un enumerador que lee el archivo línea por línea y no carga todo el archivo en la memoria, léelo nuevamente en la RAM.
Yogee
9
Sí, nadie trabaja nunca con archivos de 4GB +. Ciertamente, nunca tratamos con archivos de registro tan grandes. Oh espera.
Greg Beech
2
Si desea ver el interior de File.ReadLines () vaya aquí: System.IO.File.cs Cuando profundice en las sobrecargas, lo llevará aquí: ReadLinesIterator.cs
Steve Kinyon
12

Lo más fácil:

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

Esto usaría menos memoria, pero probablemente tomaría más tiempo

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

¿Si por fácil te refieres a líneas de código que son fáciles de descifrar pero por casualidad ineficientes?

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

Esa es probablemente la forma más rápida de saber cuántas líneas.

También podría hacerlo (dependiendo de si lo está almacenando en el búfer)

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

Hay otras formas numerosas, pero una de las anteriores es probablemente con la que irá.

usuario8456
fuente
3
Sostengo que este método es muy ineficiente; porque estás leyendo todo el archivo en la memoria y en una matriz de cadenas, nada menos. No tiene que copiar el búfer cuando usa ReadLine. Vea la respuesta de @GregBeech. Lamento llover en tu desfile.
Mike Christian
2

Puede leerlo rápidamente e incrementar un contador, solo use un bucle para incrementar, sin hacer nada con el texto.

Mitchel Sellers
fuente
3
Esto debería ser un comentario, no una respuesta.
IamBatman
2

La lectura de un archivo en sí mismo lleva algo de tiempo, la recolección de basura del resultado es otro problema al leer todo el archivo solo para contar los caracteres de nueva línea,

En algún momento, alguien tendrá que leer los caracteres en el archivo, independientemente de si este es el marco o si es su código. Esto significa que tiene que abrir el archivo y leerlo en la memoria si el archivo es grande, esto puede ser un problema ya que la memoria debe ser recolectada de basura.

Nima Ara hizo un buen análisis que podrías tener en cuenta

Aquí está la solución propuesta, ya que lee 4 caracteres a la vez, cuenta el carácter de avance de línea y vuelve a usar la misma dirección de memoria para la próxima comparación de caracteres.

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

Arriba puede ver que una línea se lee un carácter a la vez también por el marco subyacente, ya que necesita leer todos los caracteres para ver el avance de línea.

Si lo perfila como hecho bay Nima, verá que esta es una forma bastante rápida y eficiente de hacerlo.

Walter Vehoeven
fuente
1

cuente los retornos de carro / saltos de línea. Creo que en Unicode siguen siendo 0x000D y 0x000A respectivamente. de esa manera puedes ser tan eficiente o tan ineficiente como quieras y decidir si tienes que lidiar con ambos personajes o no

geocoin
fuente
1

Una opción viable, y una que he usado personalmente, sería agregar su propio encabezado a la primera línea del archivo. Hice esto para un formato de modelo personalizado para mi juego. Básicamente, tengo una herramienta que optimiza mis archivos .obj, eliminando la basura que no necesito, los convierte en un mejor diseño y luego escribe el número total de líneas, caras, normales, vértices y texturas UV en La primera línea. Esa información es utilizada por varios buffers de matriz cuando se carga el modelo.

Esto también es útil porque solo necesita recorrer el archivo una vez para cargarlo, en lugar de una vez para contar las líneas, y nuevamente para leer los datos en los búferes creados.

Krythic
fuente
-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
fuente
55
-1: ¡esto será LENTO, consumirá mucha memoria y le dará dificultades a GC!
ya23
-2

Puede iniciar el ejecutable " wc .exe" (viene con UnixUtils y no necesita instalación) como un proceso externo. Admite diferentes métodos de recuento de líneas (como unix vs mac vs windows).

Sklivvz
fuente
1
No hay forma de que esto sea lo suficientemente rápido como para ser útil. La sobrecarga de solo llamar al ejecutable sería el doble (la exageración obvia es obvia) como un solo ciclo de incremento.
Krythic