Визначте кількість рядків у текстовому файлі


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
FYI: Для того, щоб це зробити, ReadLines().Count()вам потрібно буде додати using System.Linqдо своїх включень. Здавалося, досить неінтуїтивно вимагати цього доповнення, тому я його згадую. Якщо ви використовуєте Visual Studio, швидше за все, це додавання буде зроблено автоматично.
Нуклеон

2
Я перевірив обидва підходи, "File.ReadLines.Count ()" v / s "reader.ReadLine ()" і "reader.ReadLine ()" трохи швидше, але швидше з дуже невеликим відривом. "ReadAllLines" - більш сильний, що займає вдвічі більше часу і з'їдає багато пам'яті). Це тому, що "File.ReadLines.Count ()" і "reader.ReadLine ()" - це перелік, який читає файл рядок за рядком і не завантажує весь файл у пам'ять, читаючи його знову в оперативній пам'яті.
Йоге

9
Так, ніхто не працює з файлами 4 ГБ +. Ми, звичайно, ніколи не маємо великих файлів журналів. Чекай.
Грег Бук

2
Якщо ви хочете побачити нутрощі File.ReadLines (), перейдіть сюди: System.IO.File.cs Коли ви переглядаєте перевантаження, це вас переводить сюди: ReadLinesIterator.cs
Стів Кіньйон


8

Це зайняло б менше пам'яті, але, ймовірно, займе більше часу

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

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

Є й інші численні способи, але один із перерахованих вище - це, мабуть, те, з чим ви підете.


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, позбавляючись від непотрібних мені лайнів, перетворює їх на кращу компоновку, а потім записує загальну кількість рядків, облич, нормалів, вершин і текстурних УФ на найперший рядок. Ці дані потім використовуються різними буферами масиву під час завантаження моделі.

Це також корисно, оскільки вам потрібно лише один раз прокрутити файл, щоб завантажити його, замість того, щоб один раз рахувати рядки, і знову читати дані у створених буферах.


-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 важкий час!
ya23,

-2

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


1
Неможливо, щоб це було досить швидко, щоб бути корисним. Накладні витрати лише на виклик виконуваного файлу були б удвічі більшими (очевидне перебільшення очевидне), ніж одна наростаюча петля.
Критичний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.