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

209

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

ТЗ.
источник

Ответы:

396

Серьезно запоздалое редактирование: если вы используете .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 может быть быстрее для небольших файлов, но значительно медленнее для больших файлов; хотя единственный способ определить это - измерить его с помощью секундомера или профилировщика кода.

Грег Бич
источник
2
Небольшое примечание: поскольку String является ссылочным типом, массив будет иметь размер числа строк x размера указателя, но вы правы в том, что ему все равно нужно хранить текст, каждая строка как отдельный объект String.
Майк Диммик
16
К вашему сведению: чтобы сделать это, ReadLines().Count()вам нужно добавить using System.Linqв свои списки. Казалось довольно не интуитивным требовать этого дополнения, поэтому я и упоминаю об этом. Если вы используете Visual Studio, скорее всего, это дополнение будет сделано автоматически.
Нуклон
2
Я протестировал оба подхода: «File.ReadLines.Count ()» v / s «reader.ReadLine ()» и «reader.ReadLine ()» немного быстрее, но быстрее с очень небольшим запасом. «ReadAllLines» является более свободным, который занимает вдвое больше времени и потребляет много памяти). Это потому, что «File.ReadLines.Count ()» и «reader.ReadLine ()» - это перечислитель, который читает файл построчно и не загружает весь файл в память, снова считывает его в ОЗУ.
Йоги
9
Да, никто никогда не работает с файлами 4GB +. Конечно, мы никогда не имеем дело с такими большими файлами журналов. Ой, подожди.
Грег Бич
2
Если вы хотите увидеть внутренности File.ReadLines (), перейдите по ссылке: System.IO.File.cs. Когда вы детализируете перегрузки, вы попадете сюда: ReadLinesIterator.cs
Стив Киньон,
12

Самый простой:

int lines = File.ReadAllLines("myfile").Length;
leppie
источник
8

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

int count = 0;
string line;
TextReader reader = new StreamReader("file.txt");
while ((line = reader.ReadLine()) != null)
{
  count++;
}
reader.Close();
benPearce
источник
5

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

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

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

user8456
источник
3
Я утверждаю, что этот метод очень неэффективен; потому что вы читаете весь файл в память и в строковый массив, не меньше. Вам не нужно копировать буфер при использовании ReadLine. Смотрите ответ от @GregBeech. Извините за дождь на вашем параде.
Майк Кристиан
2

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

Митчел Селлерс
источник
3
Это должен быть комментарий, а не ответ.
IamBatman
2

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

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

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

Вот предлагаемое решение, так как оно считывает 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;
}

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

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

Уолтер Веховен
источник
1

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

геокоин
источник
1

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

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

Krythic
источник
-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: это будет МЕДЛЕННО, потреблять много памяти и дать GC трудное время!
я23
-2

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

Sklivvz
источник
1
Нет никакого способа, которым это было бы достаточно быстро, чтобы быть полезным. Затраты на простой вызов исполняемого файла будут вдвое больше (очевидное преувеличение очевидно), чем один инкрементный цикл.
Krythic