Определить количество строк в текстовом файле

Есть ли простой способ программно определить количество строк в текстовом файле?

13 ответов

Решение

Серьезно запоздалое редактирование: если вы используете.NET 4.0 или новее

File класс имеет новый ReadLines метод, который лениво перечисляет строки, а не жадно читает их все в массив, как ReadAllLines, Так что теперь вы можете иметь как эффективность и краткость с:

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

Оригинальный ответ

Если вы не слишком обеспокоены эффективностью, вы можете просто написать:

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

Для более эффективного метода вы можете сделать:

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

Изменить: В ответ на вопросы об эффективности

Причина, по которой я сказал, что вторая была более эффективной, касалась использования памяти, а не обязательно скорости. Первый загружает все содержимое файла в массив, что означает, что он должен выделять как минимум столько же памяти, сколько размер файла. Вторая просто зацикливает по одной строке за раз, поэтому ей никогда не нужно выделять больше памяти, чем одна строка за раз. Это не так важно для маленьких файлов, но для больших файлов это может быть проблемой (например, если вы попытаетесь найти число строк в файле 4 ГБ в 32-битной системе, например, там, где его просто недостаточно) адресное пространство пользовательского режима для выделения такого большого массива).

Что касается скорости, я бы не ожидал, что в ней будет много. Возможно, что ReadAllLines имеет некоторую внутреннюю оптимизацию, но с другой стороны, возможно, ему придется выделить большой кусок памяти. Я предполагаю, что ReadAllLines может быть быстрее для небольших файлов, но значительно медленнее для больших файлов; хотя единственный способ определить это - измерить его с помощью секундомера или профилировщика кода.

Самый легкий:

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

Это будет использовать меньше памяти, но, вероятно, займет больше времени

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

Чтение файла само по себе занимает некоторое время, сбор мусора в результате - еще одна проблема, так как вы читаете весь файл только для того, чтобы сосчитать символы новой строки,

В какой-то момент кому-то придется читать символы в файле, независимо от того, является ли это структурой или это ваш код. Это означает, что вы должны открыть файл и прочитать его в память, если файл большой, это может стать проблемой, так как память должна собирать мусор.

Нима Ара сделал хороший анализ, который вы могли бы принять во внимание

Вот предлагаемое решение, так как оно считывает 4 символа за раз, считает символ перевода строки и снова использует тот же адрес памяти для следующего сравнения символов.

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

Выше вы можете видеть, что строка читается по одному символу за раз также базовой платформой, так как вам нужно прочитать все символы, чтобы увидеть перевод строки.

Если вы профилируете это как готовый залив Nima, вы увидите, что это довольно быстрый и эффективный способ сделать это.

Если под легким вы подразумеваете строки кода, которые легко расшифровать, но, по всей вероятности, неэффективны?

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

Это, наверное, самый быстрый способ узнать, сколько строк.

Вы также можете сделать (в зависимости от того, буферизуете ли вы его)

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

Существуют и другие многочисленные способы, но, вероятно, вы пойдете одним из вышеперечисленных.

Вы можете быстро прочитать его и увеличить счетчик, просто используя цикл для увеличения, ничего не делая с текстом.

Возможным вариантом, который я лично использовал, было бы добавление собственного заголовка в первую строку файла. Я сделал это для пользовательского формата модели для моей игры. По сути, у меня есть инструмент, который оптимизирует мои файлы.obj, избавляя от ненужной мне ненужной ерунды, преобразует их в лучший макет, а затем записывает общее количество линий, граней, нормалей, вершин и текстурных UV-объектов на самая первая строка. Эти данные затем используются различными буферами массива при загрузке модели.

Это также полезно, потому что вам нужно только один раз просмотреть файл, чтобы загрузить его, а не один раз, чтобы посчитать строки, и снова, чтобы прочитать данные в созданные вами буферы.

Рассчитывать возврат каретки / перевод строки. Я верю в Unicode они все еще 0x000D и 0x000A соответственно. Таким образом, вы можете быть настолько эффективными или неэффективными, как вы хотите, и решить, должны ли вы иметь дело с обоими персонажами или нет

Выбранный ответ меня устраивает, но мне нужно было изменитьсяvarкlongдля огромных текстовых файлов, поэтому код выглядит следующим образом:

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

В другом случае int будет переходить от отрицательных значений, что портит счет.

Также я думаю о решении, которое подсчитывает количество переводов строк (LF) в файле - чтение двоичного файла по 1 МБ или 100 МБ (зависит от памяти), а не чтение его построчно с помощью функций С#.

РЕДАКТИРОВАТЬ:

Я написал этот код:

      var sr = new StreamReader(file);
int rb = 100 * 1024 * 1024;
char[] buf = new char[rb];
int lf = 0;
int taken = 0;
while ((taken = sr.ReadBlock(buf, 0, rb)) != 0)
{
    lf += buf.Take(taken).Count(x => x == '\x0a');
}

Вроде не быстрее...

Использовать это:

    int get_lines(string file)
    {
        var lineCount = 0;
        using (var stream = new StreamReader(file))
        {
            while (stream.ReadLine() != null)
            {
                lineCount++;
            }
        }
        return lineCount;
    }
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);
}         

Я пробовал разные способы, и самый быстрый, если у вас очень большой файл:

var counter = 0;
using (var file = new StreamReader(@"file.txt"))
{
    while (file.ReadLine() != null)
    {
        counter++;
    }
}

Вы можете запустить исполняемый файл " wc.exe" (поставляется с UnixUtils и не требует установки), запущенный как внешний процесс. Он поддерживает различные методы подсчета строк (например, Unix против Mac против Windows).

Другие вопросы по тегам