For a small personal project I have been developing a parser, as part of the parser I developed the LineNumberReader modeled after the Java(TM) class of the same name. Since the class has been useful, I thought I would share it. For the purposes of the post I have removed comments and a number of the constructors.
One of the nice features is the ability to set a marker in the stream using the Mark() method and returning to that point in the stream with the Reset() method. Enjoy!
1: class LineNumberReader : StreamReader
2: {
3: public LineNumberReader(Stream stream) : base(stream){}
4:
5: public LineNumberReader(
6: Stream stream,
7: System.Text.Encoding encoding) : base(stream, encoding){}
8: // More constructors matching those from StreamReader ...
9:
10: public int LineNumber
11: {
12: get { return _lineNumber; }
13: set
14: {
15: if (value < 0 ) throw new ArgumentOutOfRangeException(
16: "LineNumber", "Must be greater or equal to 0.");
17: _lineNumber = value;
18: }
19: }
20:
21: public void Mark()
22: {
23: if (!BaseStream.CanSeek) throw new NotSupportedException(
24: "Mark is not supported, underlying stream is not seekable.");
25: _mark = new MarkData(_lineNumber, BaseStream.Position);
26: _markset = true;
27: }
28:
29: public void Reset()
30: {
31: if (!_markset) throw new IOException("Reader not marked");
32: _markset = false;
33: DiscardBufferedData();
34: BaseStream.Seek(_mark.Position, SeekOrigin.Begin);
35: _lineNumber = _mark.LineNumber;
36: }
37:
38: public override int Read()
39: {
40: int retval = base.Read();
41: if (retval == (int)'\r')
42: {
43: if (Peek() == (int)'\n')
44: {
45: base.Read();
46: }
47: retval = (int)'\n';
48: }
49:
50: if (retval == (int)'\n')
51: _lineNumber++;
52:
53: return retval;
54: }
55:
56: public override int Read(char[] buffer, int index, int count)
57: {
58: int bytesRead = base.Read(buffer, index, count);
59: _lineNumber += CountLinesInBuffer(buffer, index, bytesRead);
60: return bytesRead;
61: }
62:
63: public override string ReadLine()
64: {
65: string retval = base.ReadLine();
66: if (retval != null)
67: _lineNumber++;
68: return retval;
69: }
70:
71: private int CountLinesInBuffer(char[] buffer, int index, int count)
72: {
73: int lineCount = 0;
74: int i = index;
75: int lastIndex = index + count;
76:
77: do
78: {
79: char ch = buffer[i];
80: if (ch == '\r')
81: {
82: if ((i + 1 < lastIndex) && (buffer[i + 1] == '\n'))
83: i++;
84:
85: ch = '\n';
86: }
87:
88: if (ch == '\n')
89: lineCount++;
90: } while (++i < lastIndex);
91:
92: return lineCount;
93: }
94:
95: private struct MarkData
96: {
97: public MarkData(int lineNumber, long position)
98: {
99: LineNumber = lineNumber;
100: Position = position;
101: }
102: public int LineNumber;
103: public long Position;
104: }
105:
106: private int _lineNumber = 0;
107: private MarkData _mark;
108: private bool _markset = false;
109: }