एक पाठ फ़ाइल के भीतर लाइनों की संख्या निर्धारित करें


209

क्या किसी पाठ फ़ाइल के भीतर लाइनों की संख्या को प्रोग्रामेटिक रूप से निर्धारित करने का एक आसान तरीका है?

जवाबों:


396

गंभीर रूप से बेलेट एडिट: यदि आप .NET 4.0 या बाद में उपयोग कर रहे हैं

Fileवर्ग एक नया है ReadLinesविधि है जो lazily लालच से उन सब की तरह एक सरणी में पढ़ने के बजाय लाइनों विश्लेषण करता है 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++;
    }
}

संपादित करें: दक्षता के बारे में सवालों के जवाब में

जिस कारण से मैंने कहा कि दूसरा अधिक कुशल था, मेमोरी उपयोग के बारे में था, जरूरी नहीं कि गति। पहले वाला फ़ाइल की संपूर्ण सामग्री को एक ऐसे सरणी में लोड करता है, जिसका अर्थ है कि उसे फ़ाइल के आकार के अनुसार कम से कम मेमोरी आवंटित करनी चाहिए। दूसरा केवल एक समय में एक पंक्ति को लूप करता है, इसलिए इसे एक बार में एक से अधिक लाइन के लायक मेमोरी आवंटित नहीं करना पड़ता है। यह छोटी फ़ाइलों के लिए महत्वपूर्ण नहीं है, लेकिन बड़ी फ़ाइलों के लिए यह एक समस्या हो सकती है (यदि आप 32-बिट सिस्टम पर 4GB फ़ाइल में लाइनों की संख्या पाते हैं, उदाहरण के लिए, जहां बस पर्याप्त नहीं है उपयोगकर्ता-मोड पते की जगह एक सरणी को आवंटित करने के लिए यह बड़ा)।

गति के संदर्भ में मुझे उम्मीद नहीं है कि इसमें बहुत कुछ होगा। यह संभव है कि ReadAllLines में कुछ आंतरिक ऑप्टिमाइज़ेशन हों, लेकिन दूसरी ओर इसे मेमोरी का एक बड़ा हिस्सा आवंटित करना पड़ सकता है। मुझे लगता है कि ReadAllLines छोटी फ़ाइलों के लिए तेज़ हो सकती है, लेकिन बड़ी फ़ाइलों के लिए काफी धीमी है; हालांकि यह बताने का एकमात्र तरीका स्टॉपवॉच या कोड प्रोफाइलर के साथ मापना होगा।


2
छोटा नोट: क्योंकि स्ट्रिंग एक संदर्भ प्रकार है, सरणी एक पॉइंटर के आकार x की पंक्तियों की संख्या का आकार होगा, लेकिन आप सही हैं कि इसे अभी भी पाठ को संग्रहीत करने की आवश्यकता है, प्रत्येक पंक्ति एकल स्ट्रिंग ऑब्जेक्ट के रूप में।
माइक डिम्मिक

16
FYI करें: ऐसा करने के लिए ReadLines().Count()आपको using System.Linqअपने शामिल करने की आवश्यकता होगी । उस जोड़ की आवश्यकता के लिए यह काफी गैर-सहज लग रहा था, इसलिए मैंने इसका उल्लेख किया। यदि आपका विजुअल स्टूडियो उपयोग कर रहा है, तो यह संभव है कि यह जोड़ आपके लिए अपने आप हो जाए।
न्यूक्लियॉन

2
मैंने दोनों दृष्टिकोणों का परीक्षण किया है, "File.ReadLines.Count ()" v / s "रीडर। रीडलाइन ()" और "रीडर। रेडलाइन ()" थोड़ा तेज है लेकिन यह बहुत कम मार्जिन से तेज है। "ReadAllLines" शिथिल है जो दोगुना समय लेता है और बहुत सारी मेमोरी खाता है)। ऐसा इसलिए है क्योंकि "File.ReadLines.Count ()" और "Reader.ReadLine ()" एक एन्यूमरेटर है जो लाइन द्वारा फाइल लाइन पढ़ता है और मेमोरी में पूरी फाइल को दोबारा रैम में लोड नहीं करता है।
योगी

9
हाँ, कोई भी कभी भी 4GB + फ़ाइलों के साथ काम नहीं करता है। हम निश्चित रूप से लॉग फ़ाइलों के साथ सौदा नहीं करते हैं जो बड़े हैं। अरे रुको।
ग्रेग बीच

2
यदि आप File.ReadLines () में जाना चाहते हैं: System.IO.File.cs को जब आप ओवरलोड के माध्यम से ड्रिल करते हैं, तो आप इसे यहाँ ले जाते हैं: ReadLinesIterator.cs
स्टीव कियोन 17-16


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
यह एक टिप्पणी होनी चाहिए, एक जवाब नहीं।
46बेटमैन

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

गाड़ी के रिटर्न / लाइन फीड की गणना करें। मेरा मानना ​​है कि यूनिकोड में वे क्रमशः 0x000D और 0x000A हैं। इस तरह से आप जितना चाहें उतना कुशल या अक्षम हो सकते हैं, और यह तय कर सकते हैं कि आपको दोनों पात्रों से निपटना है या नहीं


1

एक व्यवहार्य विकल्प, और एक जो मैंने व्यक्तिगत रूप से उपयोग किया है, वह फ़ाइल की पहली पंक्ति में अपने हेडर को जोड़ना होगा। मैंने अपने खेल के लिए एक कस्टम मॉडल प्रारूप के लिए ऐसा किया। असल में, मेरे पास एक उपकरण है जो मेरी .obj फ़ाइलों का अनुकूलन करता है, जिस बकवास की मुझे ज़रूरत नहीं है उससे छुटकारा पाने के लिए, उन्हें एक बेहतर लेआउट में परिवर्तित करता है, और फिर कुल पंक्तियों, चेहरों, मानदंडों, कोने और बनावट UVs पर लिखता है बहुत पहली पंक्ति। मॉडल लोड होने पर विभिन्न डेटा बफ़र्स द्वारा उस डेटा का उपयोग किया जाता है।

यह इसलिए भी उपयोगी है क्योंकि आपको केवल लाइनों को गिनने के लिए एक बार फ़ाइल के माध्यम से लूप की आवश्यकता होती है, एक बार लाइनों को गिनने के लिए, और फिर से आपके बनाए बफ़र्स में डेटा पढ़ने के लिए।


-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: यह धीमी गति से होगा, बहुत सारी मेमोरी का उपभोग करेगा और जीसी को कठिन समय देगा!
23

-2

आप "शुरू कर सकते हैं WC .exe" निष्पादन योग्य (के साथ आता है UnixUtils एक बाहरी प्रक्रिया के रूप में और स्थापना की आवश्यकता नहीं है) रन। यह विभिन्न लाइन काउंट तरीकों (जैसे यूनिक्स बनाम मैक बनाम विंडोज़) का समर्थन करता है।


1
कोई तरीका नहीं है कि यह तेजी से उपयोगी होगा। केवल निष्पादन योग्य को बुलाने का ओवरहेड एक एकल वृद्धिशील लूप के रूप में दोगुना (स्पष्ट अतिशयोक्ति स्पष्ट है) होगा।
क्रिएथिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.