Determinar o número de linhas em um arquivo de texto


209

Existe uma maneira fácil de determinar programaticamente o número de linhas em um arquivo de texto?

Respostas:


396

Edição seriamente tardia: se você estiver usando o .NET 4.0 ou posterior

A Fileclasse possui um novo ReadLinesmétodo que enumera preguiçosamente as linhas, em vez de lê-las avidamente em uma matriz semelhante ReadAllLines. Portanto, agora você pode ter eficiência e concisão com:

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

Resposta original

Se você não está muito preocupado com a eficiência, basta escrever:

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

Para um método mais eficiente, você pode fazer:

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

Edit: Em resposta a perguntas sobre eficiência

A razão pela qual eu disse que o segundo era mais eficiente foi em relação ao uso de memória, não necessariamente à velocidade. O primeiro carrega todo o conteúdo do arquivo em uma matriz, o que significa que ele deve alocar pelo menos tanta memória quanto o tamanho do arquivo. O segundo apenas faz um loop de uma linha de cada vez, para nunca precisar alocar mais do que uma linha de memória por vez. Isso não é importante para arquivos pequenos, mas para arquivos maiores pode ser um problema (se você tentar encontrar o número de linhas em um arquivo de 4 GB em um sistema de 32 bits, por exemplo, onde simplesmente não há o suficiente espaço de endereço no modo de usuário para alocar uma matriz desse tamanho).

Em termos de velocidade, eu não esperaria que houvesse muito nele. É possível que o ReadAllLines tenha algumas otimizações internas, mas, por outro lado, pode ser necessário alocar um grande pedaço de memória. Eu acho que o ReadAllLines pode ser mais rápido para arquivos pequenos, mas significativamente mais lento para arquivos grandes; embora a única maneira de saber seja medi-lo com um cronômetro ou um criador de perfil de código.


2
Nota pequena: como String é um tipo de referência, a matriz teria o tamanho do número de linhas x o tamanho de um ponteiro, mas você está certo de que ainda precisa armazenar o texto, cada linha como um único objeto String.
Mike Dimmick

15
FYI: Para fazer ReadLines().Count()isso, você precisará adicionar um using System.Linqàs suas inclusões. Parecia bastante intuitivo exigir essa adição, e é por isso que eu a menciono. Se você estiver usando o Visual Studio, é provável que essa adição seja feita automaticamente.
Nucleon

2
Eu testei as duas abordagens: "File.ReadLines.Count ()" v / s "reader.ReadLine ()" e "reader.ReadLine ()" é um pouco mais rápido, mas é mais rápido por uma margem muito pequena. "ReadAllLines" é mais flexível, o que leva o dobro do tempo e consome muita memória). Isso ocorre porque "File.ReadLines.Count ()" e "reader.ReadLine ()" são um enumerador que lê o arquivo linha por linha e não carrega o arquivo inteiro na memória, lê-o novamente na RAM.
Yogee

9
Sim, ninguém nunca trabalha com arquivos de 4 GB ou mais. Certamente nunca lidamos com arquivos de log tão grandes. Oh espere.
Greg Beech

2
Se você quiser ver o interior de File.ReadLines (), clique aqui: System.IO.File.cs Quando você drill down através das sobrecargas que você leva aqui: ReadLinesIterator.cs
Steve Kinyon

12

O mais fácil:

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

8

Isso usaria menos memória, mas provavelmente levaria mais tempo

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

5

Se por fácil você quer dizer linhas de código fáceis de decifrar, mas por acaso ineficientes?

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

Essa é provavelmente a maneira mais rápida de saber quantas linhas.

Você também pode fazer (dependendo se você está armazenando o buffer)

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

Existem outras maneiras, mas uma das opções acima é provavelmente a que você irá usar.


3
Eu argumento que este método é muito ineficiente; porque você está lendo o arquivo inteiro na memória e em uma matriz de seqüências de caracteres. Você não precisa copiar o buffer ao usar o ReadLine. Veja a resposta de @GregBeech. Desculpe chover no seu desfile.
Mike Christian

2

Você pode ler rapidamente e incrementar um contador, basta usar um loop para incrementar, sem fazer nada com o texto.


3
Isso deve ser um comentário, não uma resposta.
IamBatman #

2

A leitura de um arquivo por si só leva algum tempo, a coleta de lixo é outro problema, à medida que você lê o arquivo inteiro apenas para contar os caracteres da nova linha,

Em algum momento, alguém terá que ler os caracteres no arquivo, independentemente se esse for o framework ou se for o seu código. Isso significa que você precisa abrir o arquivo e lê-lo na memória, se o arquivo for grande, isso poderá ser um problema, pois a memória precisa ser coletada como lixo.

Nima Ara fez uma boa análise que você pode levar em consideração

Aqui está a solução proposta, que lê 4 caracteres por vez, conta o caractere de alimentação de linha e reutiliza o mesmo endereço de memória novamente para a próxima comparação 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;
}

Acima, você pode ver que uma linha é lida com um caractere de cada vez, além da estrutura subjacente, pois você precisa ler todos os caracteres para ver o feed da linha.

Se você criar um perfil como o bay Nima concluído, verá que essa é uma maneira bastante rápida e eficiente de fazer isso.


1

conte os retornos de carro / avanços de linha. Eu acredito em unicode eles ainda são 0x000D e 0x000A, respectivamente. Dessa forma, você pode ser tão eficiente ou ineficiente quanto quiser e decidir se precisa lidar com os dois personagens ou não.


1

Uma opção viável, e que eu pessoalmente usei, seria adicionar seu próprio cabeçalho à primeira linha do arquivo. Eu fiz isso para um formato de modelo personalizado para o meu jogo. Basicamente, eu tenho uma ferramenta que otimiza meus arquivos .obj, livrando-se da porcaria de que não preciso, os converte em um layout melhor e depois grava o número total de linhas, faces, normais, vértices e UVs de textura em a primeira linha. Esses dados são usados ​​por vários buffers de matriz quando o modelo é carregado.

Isso também é útil porque você só precisa percorrer o arquivo uma vez para carregá-lo, em vez de contar uma vez as linhas e novamente ler os dados nos buffers criados.


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

4
-1: será LENTO, consumirá muita memória e dificultará o GC!
ya23

-2

Você pode iniciar o executável " wc .exe" (fornecido com o UnixUtils e não precisa de instalação) executado como um processo externo. Ele suporta diferentes métodos de contagem de linhas (como unix vs mac vs windows).


Não há como isso ser rápido o suficiente para ser útil. A sobrecarga de apenas chamar o executável seria duas vezes maior (exagero óbvio é óbvio) que um único loop de incremento.
Krythic 20/05
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.