टेक्स्ट फ़ाइल लाइन-बाय-लाइन पढ़ने का सबसे तेज़ तरीका क्या है?


318

मैं लाइन से एक पाठ फ़ाइल लाइन पढ़ना चाहता हूं। मैं जानना चाहता था कि क्या मैं इसे .NET सी # चीजों के दायरे में यथासंभव कुशलता से कर रहा हूं।

यह मैं अभी तक कोशिश कर रहा हूँ:

var filestream = new System.IO.FileStream(textFilePath,
                                          System.IO.FileMode.Open,
                                          System.IO.FileAccess.Read,
                                          System.IO.FileShare.ReadWrite);
var file = new System.IO.StreamReader(filestream, System.Text.Encoding.UTF8, true, 128);

while ((lineOfText = file.ReadLine()) != null)
{
    //Do something with the lineOfText
}

7
तक Fastestआप प्रदर्शन या विकास दृष्टिकोण से क्या मतलब है?
sll

1
यह विधि की अवधि के लिए फ़ाइल को लॉक करने जा रहा है। आप एक सरणी में File.ReadAllLines का उपयोग कर सकते हैं फिर सरणी को संसाधित कर सकते हैं।
केल

17
BTW, बंद फ़ाइल संभाल के साथ संभव कष्टप्रद मुद्दों से बचने के लिए बयान filestream = new FileStreamमें संलग्न करेंusing()
sll

फाइलस्ट्रीम को संलग्न करने के संबंध में () कथन का उपयोग करें, अनुशंसित विधि के बारे में StackOverflow देखें: StackOverflow स्टेटमेंट फाइलस्ट्रीम स्ट्रीमर
deegee

मुझे लगता है कि ReadToEnd () तेज है।
डेन जिफर्ड

जवाबों:


315

फ़ाइल लाइन को पढ़ने के लिए सबसे तेज़ तरीका खोजने के लिए आपको कुछ बेंचमार्किंग करनी होगी। मैंने अपने कंप्यूटर पर कुछ छोटे परीक्षण किए हैं लेकिन आप उम्मीद नहीं कर सकते कि मेरे परिणाम आपके पर्यावरण पर लागू होंगे।

StreamReader.ReadLine का उपयोग करना

यह मूल रूप से आपकी विधि है। किसी कारण से आप बफर आकार को सबसे छोटे संभव मान (128) पर सेट करते हैं। इसे बढ़ाने से सामान्य प्रदर्शन में वृद्धि होगी। डिफ़ॉल्ट आकार 1,024 और अन्य अच्छे विकल्प 512 (विंडोज में सेक्टर आकार) या 4,096 (NTFS में क्लस्टर आकार) हैं। एक इष्टतम बफर आकार निर्धारित करने के लिए आपको एक बेंचमार्क चलाना होगा। एक बड़ा बफर है - यदि तेज नहीं है - कम से कम एक छोटे बफर की तुलना में धीमा नहीं है।

const Int32 BufferSize = 128;
using (var fileStream = File.OpenRead(fileName))
  using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize)) {
    String line;
    while ((line = streamReader.ReadLine()) != null)
      // Process line
  }

FileStreamनिर्माता आप निर्दिष्ट कर सकते हैं FileOptions । उदाहरण के लिए, यदि आप शुरू से अंत तक एक बड़ी फाइल को क्रमिक रूप से पढ़ रहे हैं, तो आपको लाभ हो सकता है FileOptions.SequentialScan। फिर, बेंचमार्किंग सबसे अच्छी चीज है जो आप कर सकते हैं।

File.ReadLines का उपयोग करना

यह आपके अपने समाधान की तरह बहुत है सिवाय इसके कि इसे StreamReader1,024 के निश्चित बफर आकार के साथ लागू किया गया है । मेरे कंप्यूटर पर यह 128 के बफर आकार के साथ आपके कोड की तुलना में थोड़ा बेहतर प्रदर्शन करता है। हालांकि, आप बड़े बफर आकार का उपयोग करके समान प्रदर्शन वृद्धि प्राप्त कर सकते हैं। यह विधि एक इट्रेटर ब्लॉक का उपयोग करके कार्यान्वित की जाती है और सभी लाइनों के लिए मेमोरी का उपभोग नहीं करती है।

var lines = File.ReadLines(fileName);
foreach (var line in lines)
  // Process line

File.ReadAllLines का उपयोग करना

यह पिछली पद्धति की तरह बहुत है सिवाय इसके कि यह विधि लाइनों की लौटी सरणी बनाने के लिए उपयोग किए जाने वाले तार की एक सूची को बढ़ाती है ताकि स्मृति की आवश्यकताएं अधिक हों। हालाँकि, यह रिटर्न करता है String[]और IEnumerable<String>आपको लाइनों को बेतरतीब ढंग से एक्सेस करने की अनुमति नहीं देता है।

var lines = File.ReadAllLines(fileName);
for (var i = 0; i < lines.Length; i += 1) {
  var line = lines[i];
  // Process line
}

स्ट्रिंग का उपयोग कर

यह विधि काफी धीमी है, कम से कम बड़ी फ़ाइलों पर (511 KB फ़ाइल पर परीक्षण की गई), संभवतः कैसे String.Splitकार्यान्वित की गई है। यह आपके समाधान की तुलना में आवश्यक मेमोरी बढ़ाने वाली सभी लाइनों के लिए एक सरणी भी आवंटित करता है।

using (var streamReader = File.OpenText(fileName)) {
  var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
  foreach (var line in lines)
    // Process line
}

मेरा सुझाव उपयोग करना है File.ReadLinesक्योंकि यह स्वच्छ और कुशल है। यदि आपको विशेष साझाकरण विकल्प (उदाहरण के लिए आप उपयोग करते हैं FileShare.ReadWrite) की आवश्यकता है, तो आप अपने स्वयं के कोड का उपयोग कर सकते हैं लेकिन आपको बफर आकार में वृद्धि करनी चाहिए।


1
इसके लिए धन्यवाद - StreamReader के कंस्ट्रक्टर पर बफर आकार पैरामीटर का आपका समावेश वास्तव में मददगार था। मैं अमेज़ॅन के S3 एपीआई से स्ट्रीमिंग कर रहा हूं, और एक मिलान बफर आकार का उपयोग करके रीडलाइन () के साथ संयोजन के रूप में चीजों को काफी गति देता हूं।
रिचर्ड के।

मुझे समझ नहीं आ रहा है। सिद्धांत रूप में, फ़ाइल को पढ़ने में बिताया गया अधिकांश समय डिस्क पर समय मांगने और धाराओं के ओवरहेड्स की तरह होगा, जैसे आप FileReadLines के साथ क्या करेंगे। दूसरी ओर, FileReadLines, एक बार में एक फ़ाइल की सभी चीजों को मेमोरी में पढ़ने के लिए माना जाता है। यह प्रदर्शन में कैसे बदतर हो सकता है?
h9uest

2
मैं गति प्रदर्शन के बारे में नहीं कह सकता, लेकिन एक बात निश्चित है: यह मेमोरी खपत पर बहुत खराब है। यदि आपको बहुत बड़ी फ़ाइलों (उदाहरण के लिए GB) को संभालना है, तो यह बहुत महत्वपूर्ण है। इससे भी ज्यादा अगर इसका मतलब है कि इसे स्मृति को स्वैप करना है। गति की ओर, आप जोड़ सकते हैं कि ReadAllLine को सभी लाइनों को पढ़ने की आवश्यकता है, जिसके परिणामस्वरूप प्रसंस्करण में देरी हो रही है। कुछ परिदृश्यों में, गति का महत्व अधिक महत्वपूर्ण है कि कच्ची गति।
bkqc

यदि आप स्ट्रीम को बाइट सरणियों के रूप में पढ़ते हैं, तो यह फ़ाइल को 20% ~ 80% तेज़ी से (मेरे द्वारा किए गए परीक्षणों से) पढ़ेगा । बाइट सरणी प्राप्त करने और इसे स्ट्रिंग में बदलने के लिए आपको जो कुछ भी चाहिए वह है। मैंने ऐसा कैसे किया: उपयोग स्ट्रीम पढ़ने के लिए। रीड () आप इसे लूप में पढ़ने के लिए एक लूप बना सकते हैं। पूरी सामग्री को बाइट सरणी में रखने के बाद ( System.Buffer.BlockCopy का उपयोग करें ) आपको बाइट्स को स्ट्रिंग में बदलने की आवश्यकता होगी: एन्कोडिंग। ] {"\ r \ n", "\ r", "\ n"}, StringSplitOptions.None);
किम लागे

200

यदि आप .NET 4 का उपयोग कर रहे हैं, तो बस इसका उपयोग करें File.ReadLinesजो आपके लिए यह सब करता है। मुझे लगता है यह है बहुत तुम्हारा के रूप में ही है, सिवाय इसके कि यह भी उपयोग कर सकते हैं FileOptions.SequentialScanऔर एक बड़ा बफर (128 बहुत छोटी लगती है)।


इसका एक और लाभ ReadLines()यह है कि यह आलसी है इसलिए LINQ के साथ अच्छी तरह से काम करता है।
stt106

35

जबकि File.ReadAllLines()फ़ाइल को पढ़ने के सबसे सरल तरीकों में से एक है, यह सबसे धीमे में से एक भी है।

यदि आप बिना किसी फ़ाइल के लाइनों को पढ़ना चाहते हैं , तो इन बेंचमार्क के अनुसार , फ़ाइल को पढ़ने का सबसे तेज़ तरीका उम्र का पुराना तरीका है:

using (StreamReader sr = File.OpenText(fileName))
{
        string s = String.Empty;
        while ((s = sr.ReadLine()) != null)
        {
               //do minimal amount of work here
        }
}

हालांकि, यदि आपको प्रत्येक पंक्ति के साथ बहुत कुछ करना है, तो यह लेख निष्कर्ष निकालता है कि सबसे अच्छा तरीका निम्नलिखित है (और यह एक स्ट्रिंग को पूर्व-आवंटित करने के लिए तेज़ है [] यदि आपको पता है कि आप कितनी पंक्तियों को पढ़ने जा रहे हैं):

AllLines = new string[MAX]; //only allocate memory here

using (StreamReader sr = File.OpenText(fileName))
{
        int x = 0;
        while (!sr.EndOfStream)
        {
               AllLines[x] = sr.ReadLine();
               x += 1;
        }
} //Finished. Close the file

//Now parallel process each line in the file
Parallel.For(0, AllLines.Length, x =>
{
    DoYourStuff(AllLines[x]); //do your work here
});

13

निम्नलिखित कोड का उपयोग करें:

foreach (string line in File.ReadAllLines(fileName))

यह पठन प्रदर्शन में एक बड़ा अंतर था।

यह स्मृति की खपत की लागत पर आता है, लेकिन पूरी तरह से इसके लायक है!


मैं पसंद करेंगे File.ReadLines (मुझे क्लिक करें) सेFile.ReadAllLines
newbieguy

5

स्टैक ओवरफ्लो प्रश्न में इसके बारे में एक अच्छा विषय है "उपज" धीमी "पुराने स्कूल" की तुलना में धीमी है?

इसे कहते हैं:

ReadAllLines सभी लाइनों को मेमोरी में लोड करता है और एक स्ट्रिंग देता है []। यदि फ़ाइल छोटी है तो सब ठीक है और अच्छा है। यदि फ़ाइल मेमोरी से अधिक फिट होगी, तो आप मेमोरी से बाहर चलेंगे।

दूसरी ओर, ReadLines, एक समय में एक पंक्ति में लौटने के लिए उपज रिटर्न का उपयोग करता है। इसके साथ, आप किसी भी आकार की फ़ाइल पढ़ सकते हैं। यह पूरी फ़ाइल को मेमोरी में लोड नहीं करता है।

मान लें कि आप पहली पंक्ति ढूंढना चाहते हैं जिसमें "फू" शब्द है, और फिर बाहर निकलें। ReadAllLines का उपयोग करते हुए, आपको पूरी फ़ाइल को मेमोरी में पढ़ना होगा, भले ही पहली पंक्ति में "फू" हो। ReadLines के साथ, आप केवल एक पंक्ति पढ़ते हैं। कौन सा तेज होगा?


4

यदि फ़ाइल का आकार बड़ा नहीं है, तो यह पूरी फ़ाइल को पढ़ने और बाद में विभाजित करने के लिए तेज़ है

var filestreams = sr.ReadToEnd().Split(Environment.NewLine, 
                              StringSplitOptions.RemoveEmptyEntries);

6
File.ReadAllLines()
jgauffin

@jgauffin मुझे फ़ाइल के कार्यान्वयन के पीछे पता नहीं है। ReadAlllines (), लेकिन मुझे लगता है कि इसका एक सीमित बफर है और फ़ाइल रीडऑनइंड बफर अधिक होना चाहिए, इसलिए फ़ाइल तक पहुँच की संख्या इस तरह से कम हो जाएगी, और स्ट्रिंग में .plit केस फ़ाइल का आकार बड़ा नहीं है, फ़ाइल के लिए कई पहुँच से तेज़ है।
सईद अमीरी

मुझे संदेह है कि File.ReadAllLinesफ़ाइल आकार ज्ञात होने के बाद से एक निश्चित बफर आकार है।
jgauffin 14

1
@jgauffin: .NET 4.0 में File.ReadAllLinesएक सूची बनाता है और लूप का उपयोग करके इस सूची में जुड़ जाता है StreamReader.ReadLine(अंतर्निहित एरे के संभावित वास्तविककरण के साथ)। यह विधि 1024 के डिफ़ॉल्ट बफर आकार का उपयोग करती है। StreamReader.ReadToEndलाइन पार्सिंग भाग से बचा जाता है और यदि वांछित है तो बफर आकार को कंस्ट्रक्टर में सेट किया जा सकता है।
मार्टिन लीवरेज

फ़ाइल आकार के संबंध में "BIG" को परिभाषित करना मददगार होगा।
पॉल

2

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


1
File.ReadAllLinesतब एक बेहतर विकल्प लगता है।
jgauffin

2

यदि आप लाइनों को पढ़ने के लिए मौजूदा एपीआई का उपयोग करना चाहते हैं तो आपको कोई तेजी नहीं मिल सकती है। लेकिन बड़ी विखंडू को पढ़ना और मैन्युअल रूप से रीड बफर में प्रत्येक नई लाइन को ढूंढना शायद तेज होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.