2009-05-06 13 views
8

एक उदाहरण (जो वास्तविक जीवन नहीं हो सकता है, लेकिन मेरी बात करने के लिए):टेक्स्टफाइल में स्ट्रीम स्ट्रीमर की स्थिति (linenumber) को कैसे जानें?

public void StreamInfo(StreamReader p) 
{ 
    string info = string.Format(
     "The supplied streamreaer read : {0}\n at line {1}", 
     p.ReadLine(), 
     p.GetLinePosition()-1);    

} 

यहाँ GetLinePosition StreamReader की काल्पनिक विस्तार विधि है। क्या यह संभव है?

बेशक मैं खुद को गिनती रख सकता हूं लेकिन यह सवाल नहीं है।

उत्तर

7

यह किसी भी TextReader के लिए एक लाइन की गिनती आवरण प्रदान करने के लिए बहुत आसान है:

public class PositioningReader : TextReader { 
    private TextReader _inner; 
    public PositioningReader(TextReader inner) { 
     _inner = inner; 
    } 
    public override void Close() { 
     _inner.Close(); 
    } 
    public override int Peek() { 
     return _inner.Peek(); 
    } 
    public override int Read() { 
     var c = _inner.Read(); 
     if (c >= 0) 
      AdvancePosition((Char)c); 
     return c; 
    } 

    private int _linePos = 0; 
    public int LinePos { get { return _linePos; } } 

    private int _charPos = 0; 
    public int CharPos { get { return _charPos; } } 

    private int _matched = 0; 
    private void AdvancePosition(Char c) { 
     if (Environment.NewLine[_matched] == c) { 
      _matched++; 
      if (_matched == Environment.NewLine.Length) { 
       _linePos++; 
       _charPos = 0; 
       _matched = 0; 
      } 
     } 
     else { 
      _matched = 0; 
      _charPos++; 
     } 
    } 
} 

कमियां (संक्षिप्तता की खातिर):

  1. अशक्त
  2. के लिए निर्माता तर्क की जाँच नहीं करता
  3. लाइनों को समाप्त करने के वैकल्पिक तरीकों को नहीं पहचानता है। कच्चे \ r या \ n द्वारा अलग फ़ाइलों को पढ़ने के दौरान ReadLine() व्यवहार के साथ असंगत होगा।
  4. "ब्लॉक" ओवरलेड नहीं करता है -लेवल विधियों जैसे रीड (char [], int, int), रीडब्लॉक, रीडलाइन, ReadToEnd। TextReader कार्यान्वयन सही ढंग से काम करता है क्योंकि यह सबकुछ पढ़ने के लिए रूट करता है(); हालांकि,
    • द्वारा बेहतर प्रदर्शन प्राप्त किया जा सकता है, जो कि रात्रिभोज पर रूटिंग कॉल के माध्यम से उन तरीकों को ओवरराइड कर रहा है। आधार के बजाय।
    • अग्रिमपोत को पढ़ने वाले पात्रों को पारित करना।नमूना ReadBlock कार्यान्वयन देखें:

public override int ReadBlock(char[] buffer, int index, int count) { 
    var readCount = _inner.ReadBlock(buffer, index, count);  
    for (int i = 0; i < readCount; i++) 
     AdvancePosition(buffer[index + i]); 
    return readCount; 
} 
+0

यह 'सेक' का उपयोग करने के लिए भी खाता नहीं है। –

10

नहीं, वास्तव में संभव नहीं है। "लाइन नंबर" की अवधारणा वास्तविक डेटा पर आधारित है जो पहले ही पढ़ी जा चुकी है, केवल स्थिति ही नहीं। उदाहरण के लिए, यदि आप पाठक को मनमानी स्थिति में खोजना चाहते थे, तो यह उस डेटा को पढ़ने के लिए कार्य नहीं करेगा, इसलिए यह लाइन नंबर निर्धारित करने में सक्षम नहीं होगा।

ऐसा करने का एकमात्र तरीका यह है कि आप इसे ट्रैक रखें।

+1

+1 आप मेरे सामने में आया तो अब, अगर मैं सिर्फ तेजी से टाइप कर सकते हैं :) –

4

सं

पर विचार करें कि यह अंतर्निहित धारा वस्तु (जो किसी भी लाइन में किसी भी बिंदु पर हो सकता है) का उपयोग कर किसी भी poisition को तलाश करने के लिए संभव है। अब विचार करें कि StreamReader द्वारा रखी गई किसी भी गिनती के साथ क्या होगा।

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

इन दोनों से अधिक प्रश्न हैं जो इसे लागू करने के लिए एक दुःस्वप्न बनाते हैं, इमो।

+2

+1 कारण प्रतिनिधि प्राप्त कर रहा टाइपिंग की गति ;-) – Peter

+0

दूसरी ओर पर निर्भर नहीं करना चाहिए, हम प्रतिनिधि के लिए हासिल करना चाहिए दोहराना क्या पहले से ही कहा जा रहा है? (यह पोस्टर नहीं कह रहा था, लेकिन सामान्य रूप से यह निश्चित रूप से संभव होगा!) –

+0

@ द डैग: एक ही समय में कहा गया है कि इतना दोहराया नहीं जा रहा है। । । मनहूस! (बीटीडब्ल्यू, डी या एक डैग खरीदना चाहते हैं?) –

3

यहां एक ऐसा व्यक्ति है जिसने StreamLine() विधि के साथ StreamReader को कार्यान्वित किया है जो फ़ाइल स्थिति पंजीकृत करता है।

http://www.daniweb.com/forums/thread35078.html

मुझे लगता है कि एक StreamReader से विरासत चाहिए, और फिर कुछ गुण (_lineLength + _bytesRead) के साथ विशेष वर्ग के लिए अतिरिक्त पद्धति जोड़ें:

// Reads a line. A line is defined as a sequence of characters followed by 
// a carriage return ('\r'), a line feed ('\n'), or a carriage return 
// immediately followed by a line feed. The resulting string does not 
// contain the terminating carriage return and/or line feed. The returned 
// value is null if the end of the input stream has been reached. 
// 
/// <include file='doc\myStreamReader.uex' path='docs/doc[@for="myStreamReader.ReadLine"]/*' /> 
public override String ReadLine() 
{ 
      _lineLength = 0; 
      //if (stream == null) 
      //  __Error.ReaderClosed(); 
      if (charPos == charLen) 
      { 
        if (ReadBuffer() == 0) return null; 
      } 
      StringBuilder sb = null; 
      do 
      { 
        int i = charPos; 
        do 
        { 
          char ch = charBuffer[i]; 
          int EolChars = 0; 
          if (ch == '\r' || ch == '\n') 
          { 
            EolChars = 1; 
            String s; 
            if (sb != null) 
            { 
              sb.Append(charBuffer, charPos, i - charPos); 
              s = sb.ToString(); 
            } 
            else 
            { 
              s = new String(charBuffer, charPos, i - charPos); 
            } 
            charPos = i + 1; 
            if (ch == '\r' && (charPos < charLen || ReadBuffer() > 0)) 
            { 
              if (charBuffer[charPos] == '\n') 
              { 
                 charPos++; 
                 EolChars = 2; 
              } 
            } 
            _lineLength = s.Length + EolChars; 
            _bytesRead = _bytesRead + _lineLength; 
            return s; 
          } 
          i++; 
        } while (i < charLen); 
        i = charLen - charPos; 
        if (sb == null) sb = new StringBuilder(i + 80); 
        sb.Append(charBuffer, charPos, i); 
      } while (ReadBuffer() > 0); 
      string ss = sb.ToString(); 
      _lineLength = ss.Length; 
      _bytesRead = _bytesRead + _lineLength; 
      return ss; 
} 

सोचो वहाँ एक मामूली बग है कोड में स्ट्रिंग की लंबाई को वास्तविक बाइट्स पढ़ने के बजाय फ़ाइल स्थिति की गणना करने के लिए उपयोग किया जाता है (यूटीएफ 8 और यूटीएफ 16 एन्कोडेड फ़ाइलों के लिए समर्थन की कमी)।

1

अंक पहले से ही BaseStream के संबंध में किए गए वैध और महत्वपूर्ण हैं। हालांकि, ऐसी स्थितियां हैं जिनमें आप एक पाठ पढ़ना चाहते हैं और जानते हैं कि आप कहां हैं। यह अभी भी इसे पुन: उपयोग करने में आसान बनाने के लिए कक्षा के रूप में लिखने के लिए उपयोगी हो सकता है।

मैं अब इस तरह के एक वर्ग लिखने का प्रयास किया। ऐसा लगता है कि यह सही ढंग से काम करता है, लेकिन यह धीमा है। यह ठीक हो जाना चाहिए जब प्रदर्शन महत्वपूर्ण नहीं है (यह नहीं है कि धीमी गति से, नीचे देखें)।

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

मैंने StreamReader को रीडलाइन विधि (जो मुझे लगता है कि इस कार्यान्वयन का सबसे कमजोर बिंदु है) की एक बहुत ही बुनियादी प्रदर्शन तुलना की है, और अंतर लगभग परिमाण का एक क्रम है। मुझे अपनी कक्षा StreamReaderEx का उपयोग करके 22 एमबी/एस मिल गया है, लेकिन स्ट्रीमराइडर का उपयोग करके लगभग 9 गुना अधिक (मेरे एसएसडी-सुसज्जित लैपटॉप पर)। हालांकि यह दिलचस्प हो सकता है, मुझे नहीं पता कि उचित पढ़ने का परीक्षण कैसे किया जाए; शायद 2 समान फाइलों का उपयोग कर, प्रत्येक डिस्क बफर से बड़ा है, और उन्हें वैकल्पिक रूप से पढ़ रहा है ..? कम से कम मेरा सरल परीक्षण लगातार परिणाम उत्पन्न करता है जब मैं इसे कई बार चलाता हूं, और इस पर ध्यान दिए बिना कि कौन सी कक्षा पहले टेस्ट फ़ाइल पढ़ती है।

न्यूलाइन प्रतीक पर्यावरण के लिए डिफ़ॉल्ट है। न्यूलाइन लेकिन लंबाई 1 या 2 की किसी भी स्ट्रिंग पर सेट किया जा सकता है। पाठक केवल इस प्रतीक को एक नई लाइन के रूप में मानता है, जो एक दोष हो सकता है। कम से कम मुझे पता है कि विजुअल स्टूडियो ने मुझे उचित समय दिया है कि एक फ़ाइल जिसे मैं खोलता हूं "में असंगत न्यूलाइन है"।

कृपया ध्यान दें कि मैं गार्ड वर्ग शामिल नहीं है; यह एक साधारण उपयोगिता वर्ग है और इसे संदर्भ से अपवित्र होना चाहिए कि इसे कैसे बदला जाए। आप इसे भी हटा सकते हैं, लेकिन आप कुछ तर्क जांच खो देंगे और इस प्रकार परिणामी कोड "सही" से आगे होगा। उदाहरण के लिए, Guard.NotNull (रों, "एस") बस की जाँच करता है कि रों (तर्क नाम "एस", इसलिए दूसरा पैरामीटर के साथ) एक ArgumentNullException फेंकने यह मामला हो सकता है, अशक्त नहीं है।

 

public class StreamReaderEx : StreamReader 
{ 
    // NewLine characters (magic value -1: "not used"). 
    int newLine1, newLine2; 

    // The last character read was the first character of the NewLine symbol AND we are using a two-character symbol. 
    bool insideNewLine; 

    // StringBuilder used for ReadLine implementation. 
    StringBuilder lineBuilder = new StringBuilder(); 


    public StreamReaderEx(string path, string newLine = "\r\n") : base(path) 
    { 
     init(newLine); 
    } 


    public StreamReaderEx(Stream s, string newLine = "\r\n") : base(s) 
    { 
     init(newLine); 
    } 


    public string NewLine 
    { 
     get { return "" + (char)newLine1 + (char)newLine2; } 
     private set 
     { 
      Guard.NotNull(value, "value"); 
      Guard.Range(value.Length, 1, 2, "Only 1 to 2 character NewLine symbols are supported."); 

      newLine1 = value[0]; 
      newLine2 = (value.Length == 2 ? value[1] : -1); 
     } 
    } 


    public int LineNumber { get; private set; } 
    public int LinePosition { get; private set; } 


    public override int Read() 
    { 
     int next = base.Read(); 
     trackTextPosition(next); 
     return next; 
    } 


    public override int Read(char[] buffer, int index, int count) 
    { 
     int n = base.Read(buffer, index, count); 
     for (int i = 0; i 
+0

ओह महान, मेरा कोड बस बीच में कट ऑफ था। मैं यह देखने का अवसर दूंगा कि क्या कोई दिलचस्पी लेता है; यदि हां, तो मुझे बताएं और मैं शेष पोस्ट करूंगा। –

3

मैं यहाँ कुछ सरल की तलाश में आया:

पर्याप्त प्रलाप, यहाँ कोड है। तुम सिर्फ ReadLine() का उपयोग कर रहे एंड सीक() या कुछ भी उपयोग के बारे में परवाह नहीं है, तो बस StreamReader

का एक सरल उपवर्ग
class CountingReader : StreamReader { 
    private int _lineNumber = 0; 
    public int LineNumber { get { return _lineNumber; } } 

    public CountingReader(Stream stream) : base(stream) { } 

    public override string ReadLine() { 
     _lineNumber++; 
     return base.ReadLine(); 
    } 
} 

और फिर आप इसे सामान्य तरीके से बनाने के लिए, एक FileInfo वस्तु से कहना नाम फ़ाइल

CountingReader reader = new CountingReader(file.OpenRead()) 

और आप बस reader.LineNumber संपत्ति पढ़ें।

+0

अच्छा जवाब, लेकिन आपको यह स्पष्ट करना चाहिए कि यह केवल तभी काम करेगा यदि 'रीडलाइन' _only_ विधि है जिसे आप बुला रहे हैं। –

13

मैं इस पोस्ट भर में कैम, जबकि एक समान समस्या है जहाँ मैं विशेष रूप से लाइनों के लिए StreamReader की तलाश करने की जरूरत के लिए एक समाधान की तलाश में। मैंने StreamReader पर स्थिति प्राप्त करने और सेट करने के लिए दो एक्सटेंशन विधियां तैयार कीं।यह वास्तव में एक लाइन नंबर गिनती प्रदान नहीं करता है, लेकिन व्यवहार में, मैं बस प्रत्येक रीडलाइन() से पहले स्थिति को पकड़ता हूं और यदि रेखा ब्याज की है, तो मैं बाद में लाइन पर वापस जाने के लिए सेटिंग सेट करने के लिए प्रारंभ स्थिति रखता हूं :

var index = streamReader.GetPosition(); 
var line1 = streamReader.ReadLine(); 

streamReader.SetPosition(index); 
var line2 = streamReader.ReadLine(); 

Assert.AreEqual(line1, line2); 

और महत्वपूर्ण हिस्सा:

public static class StreamReaderExtensions 
{ 
    readonly static FieldInfo charPosField = typeof(StreamReader).GetField("charPos", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly); 
    readonly static FieldInfo byteLenField = typeof(StreamReader).GetField("byteLen", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly); 
    readonly static FieldInfo charBufferField = typeof(StreamReader).GetField("charBuffer", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly); 

    public static long GetPosition(this StreamReader reader) 
    { 
     //shift position back from BaseStream.Position by the number of bytes read 
     //into internal buffer. 
     int byteLen = (int)byteLenField.GetValue(reader); 
     var position = reader.BaseStream.Position - byteLen; 

     //if we have consumed chars from the buffer we need to calculate how many 
     //bytes they represent in the current encoding and add that to the position. 
     int charPos = (int)charPosField.GetValue(reader); 
     if (charPos > 0) 
     { 
      var charBuffer = (char[])charBufferField.GetValue(reader); 
      var encoding = reader.CurrentEncoding; 
      var bytesConsumed = encoding.GetBytes(charBuffer, 0, charPos).Length; 
      position += bytesConsumed; 
     } 

     return position; 
    } 

    public static void SetPosition(this StreamReader reader, long position) 
    { 
     reader.DiscardBufferedData(); 
     reader.BaseStream.Seek(position, SeekOrigin.Begin); 
    } 
} 

यह मेरे लिए काफी अच्छी तरह से काम करता है और प्रतिबिंब का उपयोग कर यह लगता है कि यह एक काफी सरल समाधान है के लिए अपनी सहिष्णुता के आधार पर।

चेतावनियां:

  1. जब मैं विभिन्न System.Text.Encoding विकल्पों का उपयोग कर कुछ सरल परीक्षण किया है, काफी सभी डेटा मैं इस के साथ उपभोग की साधारण पाठ फ़ाइलें (ASCII) कर रहे हैं।
  2. मैंने कभी भी StreamReader.ReadLine() विधि का उपयोग किया है और स्ट्रीमरडर के स्रोत के बारे में एक संक्षिप्त समीक्षा यह इंगित करती है कि यह अभी भी अन्य पढ़ने के तरीकों का उपयोग करते समय काम करेगा, मैंने वास्तव में उस परिदृश्य का परीक्षण नहीं किया है।
+0

'System.Text.Encoding.UTF8' के साथ काम करता है – CrazyIvan1974

संबंधित मुद्दे