Metin dosyasındaki satır sayısını belirleme


209

Bir metin dosyasındaki satır sayısını programlı olarak belirlemenin kolay bir yolu var mı?

Yanıtlar:


396

Cidden gecikmiş düzenleme: .NET 4.0 veya üstünü kullanıyorsanız

FileSınıfı, yeni sahiptir ReadLineslazily iştahla gibi bir diziye hepsini okuma yerine hatları sıralar yöntem ReadAllLines. Böylece şimdi hem verime hem de kısaca sahip olabilirsiniz:

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

Orijinal Yanıt

Verimlilikten çok rahatsız değilseniz, şunları yazabilirsiniz:

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

Daha verimli bir yöntem için şunları yapabilirsiniz:

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

Düzenleme: Verimlilik ile ilgili sorulara yanıt olarak

İkincisinin daha verimli olduğunu söylememin sebebi, hız değil, bellek kullanımı ile ilgilidir. Birincisi, dosyanın tüm içeriğini bir diziye yükler; bu, dosyanın boyutu kadar en az bellek ayırması gerektiği anlamına gelir. İkincisi sadece bir seferde bir satır döngüye girer, bu yüzden asla bir seferde birden fazla satırın belleğini ayırmak zorunda kalmaz. Bu, küçük dosyalar için o kadar önemli değildir, ancak daha büyük dosyalar için bir sorun olabilir (örneğin, 32 bitlik bir sistemde 4 GB'lık bir dosyadaki satır sayısını bulmaya çalışırsanız, örneğin, yeterli olmayan kullanıcı modu adres alanı bu kadar büyük bir dizi ayırmak için).

Hız açısından, içinde çok fazla bir şey olmasını beklemiyordum. ReadAllLines'in bazı dahili optimizasyonları olabilir, ancak öte yandan, büyük bir bellek yığını tahsis etmesi gerekebilir. Ben ReadAllLines küçük dosyalar için daha hızlı, ancak büyük dosyalar için önemli ölçüde daha yavaş olabilir sanırım; ancak anlatmanın tek yolu bunu bir Kronometre veya kod profiler ile ölçmek olacaktır.


2
Küçük not: Dize bir referans türü olduğundan, dizi satır sayısının x bir işaretçinin boyutu olacaktır, ancak yine de metni, her satırı tek bir String nesnesi olarak depolaması gerektiği doğrudur.
Mike Dimmick

16
Bilginize: Bunu yapabilmek ReadLines().Count()için eklentilerinize a eklemeniz gerekecektir using System.Linq. Bu eklemeyi istemek oldukça sezgisel görünmüyordu, bu yüzden bundan bahsediyorum. Visual Studio kullanıyorsanız, bu ekleme sizin için otomatik olarak yapılır.
Nucleon

2
Her iki yaklaşımı da test ettim, "File.ReadLines.Count ()" v / s "reader.ReadLine ()" ve "reader.ReadLine ()" biraz daha hızlı ama çok az farkla daha hızlı. "ReadAllLines" iki kat daha fazla zaman alır ve çok fazla bellek yiyor gevşek). Bunun nedeni, "File.ReadLines.Count ()" ve "reader.ReadLine ()", dosyaları satır satır okuyan ve tüm dosyayı yeniden RAM'de okuyan belleğe yüklemeyen bir numaralandırıcıdır.
Yogee

9
Evet, hiç kimse 4GB + 'lık dosyalarla çalışmaz. Kesinlikle bu kadar büyük günlük dosyaları ile uğraşmayacağız. Bekle.
Greg Beech

2
File.ReadLines () 'in içini görmek istiyorsanız buraya gidin: System.IO.File.cs Aşırı
yükleri incelediğinizde

12

Kolay:

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

8

Bu daha az bellek kullanır, ancak muhtemelen daha uzun sürer

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

5

Kolayca deşifre etmek kolay ama şans başına verimsiz bir kod satırları demek?

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

Muhtemelen kaç satır olduğunu bilmenin en hızlı yolu budur.

Ayrıca şunları yapabilirsiniz (arabelleğe alıp almadığınıza bağlı olarak)

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

Başka sayısız yol var ama yukarıdakilerden biri muhtemelen gideceğiniz şey.


3
Bu yöntemin çok verimsiz olduğunu iddia ediyorum; çünkü, tüm dosyayı belleğe ve bir dize dizisine okursunuz, daha az değil. ReadLine kullanırken arabelleği kopyalamanız gerekmez. @GregBeech'in cevabına bakınız. Geçit töreninde yağmur yağdırdım.
Mike Christian

2

Hızlı bir şekilde okuyabilir ve bir sayacı artırabilirsiniz, sadece artırmak için bir döngü kullanın, metinle hiçbir şey yapmayın.


3
Bu bir yorum olmalı, cevap değil.
IamBatman

2

Bir dosyayı kendi başına okumak biraz zaman alır, sonucu toplamak, yalnızca satırsonu karakterlerini saymak için tüm dosyayı okurken başka bir sorundur,

Bir noktada, birisi bu çerçevenin veya kodunuzun olup olmadığına bakılmaksızın dosyadaki karakterleri okumak zorunda kalacaktır. Bu, dosyayı açmanız ve dosya büyükse belleğe okumanız gerektiği anlamına gelir; bu, belleğin çöp toplanması gerektiğinden potansiyel olarak bir sorun olacaktır.

Nima Ara, dikkate alabileceğiniz güzel bir analiz yaptı

Burada önerilen çözüm, her seferinde 4 karakter okuduğundan, satır besleme karakterini sayar ve bir sonraki karakter karşılaştırması için aynı bellek adresini tekrar kullanır.

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

Yukarıda, satır beslemesini görmek için tüm karakterleri okumanız gerektiğinden, bir satırın altta yatan çerçeve tarafından her seferinde bir karakter okunduğunu görebilirsiniz.

Eğer bunu bay Nima olarak yapmışsanız, bunun bunu yapmanın oldukça hızlı ve etkili bir yolu olduğunu görürsünüz.


1

satırbaşı / satır beslemelerini sayın. Unicode hala 0x000D ve 0x000A olduğuna inanıyorum. bu şekilde istediğiniz kadar verimli veya verimsiz olabilirsiniz ve her iki karakterle de ilgilenip ilgilenmediğinize karar verebilirsiniz.


1

Geçerli bir seçenek ve kişisel olarak kullandığım seçenek, dosyanın ilk satırına kendi başlığınızı eklemek olacaktır. Bunu oyunum için özel bir model formatı için yaptım. Temel olarak, .obj dosyalarımı optimize eden, ihtiyacım olan saçmalıklardan kurtulan, daha iyi bir düzene dönüştüren ve daha sonra toplam satır, yüz, normal, köşe ve doku UV'lerini yazan bir aracım var ilk satır. Bu veriler daha sonra model yüklendiğinde çeşitli dizi arabellekleri tarafından kullanılır.

Bu da kullanışlıdır çünkü dosyayı yüklemek için sadece bir kez, satırları saymak yerine ve verileri oluşturduğunuz arabelleklere tekrar okumak için döngü yapmanız gerekir.


-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: bu YAVAŞ olacak, çok fazla bellek tüketecek ve GC'ye zor zaman verecektir!
ya23

-2

" Wc .exe" yürütülebilir dosyasını ( UnixUtils ile birlikte gelir ve kurulum gerektirmez) harici bir işlem olarak çalıştırabilirsiniz. Farklı satır sayısı yöntemlerini destekler (unix vs mac vs windows).


1
Bunun faydalı olacak kadar hızlı olmasının bir yolu yoktur. Sadece yürütülebilir dosyayı çağırmanın yükü, tek bir artan döngüden iki kat daha fazla (bariz abartı açıktır) olacaktır.
Krythic
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.