Search code examples
c#streamreader

How to know position(linenumber) of a streamreader in a textfile?


an example (that might not be real life, but to make my point) :

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

}

GetLinePosition here is an imaginary extension method of streamreader. Is this possible?

Of course I could keep count myself but that's not the question.


Solution

  • It is extremely easy to provide a line-counting wrapper for any 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++;
            }
        }
    }
    

    Drawbacks (for the sake of brevity):

    1. Does not check constructor argument for null
    2. Does not recognize alternate ways to terminate the lines. Will be inconsistent with ReadLine() behavior when reading files separated by raw \r or \n.
    3. Does not override "block"-level methods like Read(char[], int, int), ReadBlock, ReadLine, ReadToEnd. TextReader implementation works correctly since it routes everything else to Read(); however, better performance could be achieved by
      • overriding those methods via routing calls to _inner. instead of base.
      • passing the characters read to the AdvancePosition. See the sample ReadBlock implementation:

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