Thursday, January 11, 2007

Seekable LineReader

Recently I need a way in StreamReader to get the position of a line which can be stored and later skip to that line directly. C# StreamReader API does not have any way of doing this, except calling Read() repeatedly and then doing the processing yourself. Which is clumsy. Note that, StreamReader.BaseStream.Position might be wrong due to underlying bufferring. I thought this should be a fairly common requirement and indeed, many people have same question on google groups or other forums. No good answer though. One reason is, such a thing does not really make sense for arbitrary Streams, since it might not be possible to seek in them.

I needed such a thing desparately, so I created an interface:
namespace System.IO {

// A linereader interface
public interface LineReader {

// Returns a position marker, which can be used to navigate the lines.
// Some implementations may only allow moving in the forward direction.
// Might be different from line number or file offset.
// Should only be used for traversal.
long Position {
get;
set;
}

// Reads and returns the next line, null if EOF
string ReadLine ();

// Reads the next line and returns a stringbuilder containing the line
// The StringBuilder returned could be the same one used while reading,
// so it should not be modified and its content might change when readline
// is next called.
// This is the most worst horriblest API I ever designed, for sake of speed
// And thats why this should not be a public API.
StringBuilder ReadLineAsStringBuilder ();

// Skips the next line, return true if successful
bool SkipLine ();

// Skips required number of lines; returns actual number of lines skipped
long SkipLines (long n);

// Close the reader
void Close ();
}
}
Beagle source contains the interface and several implementations.