Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tracking the position of the line of a streamreader

I need to track the position of the line that I am reading from the stream reader. When I say reader.ReadLine(), I need to know the position of that line in the file and I also want to be able to then read the file from the position I have previously tracked.

Is this possible?

like image 422
johnnie Avatar asked Apr 17 '12 10:04

johnnie


People also ask

What does StreamReader mean?

StreamReader is designed for character input in a particular encoding, whereas the Stream class is designed for byte input and output. Use StreamReader for reading lines of information from a standard text file. Important. This type implements the IDisposable interface.

How does a StreamReader differ from a StreamWriter?

A StreamReader is used whenever data is required to be read from a file. A Streamwriter is used whenever data needs to be written to a file.

What is StreamReader and StreamWriter?

The StreamReader and StreamWriter classes are used for reading from and writing data to text files. These classes inherit from the abstract base class Stream, which supports reading and writing bytes into a file stream.


3 Answers

You can do this one of three ways:

1) Write your own StreamReader. Here's a good place to start: How to know position(linenumber) of a streamreader in a textfile?

2) The StreamReader class has two very important, but private variables called charPos and charLen that are needed in locating the actual "read" position and not just the underlying position of the stream. You could use reflection to get the values as suggested here

Int32 charpos = (Int32) s.GetType().InvokeMember("charPos", 
BindingFlags.DeclaredOnly | 
BindingFlags.Public | BindingFlags.NonPublic | 
BindingFlags.Instance | BindingFlags.GetField
 ,null, s, null); 

Int32 charlen= (Int32) s.GetType().InvokeMember("charLen", 
BindingFlags.DeclaredOnly | 
BindingFlags.Public | BindingFlags.NonPublic | 
BindingFlags.Instance | BindingFlags.GetField
 ,null, s, null);

return (Int32)s.BaseStream.Position-charlen+charpos;

3) Simply read the entire file into a string array. Something like this:

char[] CRLF = new char[2] { '\n', '\r' };
TextReader tr = File.OpenText("some path to file");
string[] fileLines = tr.ReadToEnd().Split(CRLF);

Another possibility (along the sames lines as #3) is to read in the lines and store the line in an array. When you want to read the prior line, just use the array.

like image 55
Chris Gessler Avatar answered Sep 30 '22 19:09

Chris Gessler


Tracking the actual StreamReader position (in bytes):

readonly static FieldInfo charPosField = typeof(StreamReader).GetField("charPos", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
readonly static FieldInfo charLenField = typeof(StreamReader).GetField("charLen", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
readonly static FieldInfo charBufferField = typeof(StreamReader).GetField("charBuffer", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);

static long ActualPosition(StreamReader reader)
{
    var charBuffer = (char[])charBufferField.GetValue(reader);
    var charLen = (int)charLenField.GetValue(reader);
    var charPos = (int)charPosField.GetValue(reader);

    return reader.BaseStream.Position - reader.CurrentEncoding.GetByteCount(charBuffer, charPos, charLen-charPos);
}

You are welcome.

like image 36
Serge Savel Avatar answered Sep 30 '22 20:09

Serge Savel


Maybe this can help you

  public class StreamLineReader : IDisposable
    {
        const int BufferLength = 1024;

        Stream _Base;
        int _Read = 0, _Index = 0;
        byte[] _Bff = new byte[BufferLength];

        long _CurrentPosition = 0;
        int _CurrentLine = 0;

        /// <summary>
        /// CurrentLine number
        /// </summary>
        public long CurrentPosition { get { return _CurrentPosition; } }
        /// <summary>
        /// CurrentLine number
        /// </summary>
        public int CurrentLine { get { return _CurrentLine; } }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="stream">Stream</param>
        public StreamLineReader(Stream stream) { _Base = stream; }
        /// <summary>
        /// Count lines and goto line number
        /// </summary>
        /// <param name="goToLine">Goto Line number</param>
        /// <returns>Return true if goTo sucessfully</returns>
        public bool GoToLine(int goToLine) { return IGetCount(goToLine, true) == goToLine; }
        /// <summary>
        /// Count lines and goto line number
        /// </summary>
        /// <param name="goToLine">Goto Line number</param>
        /// <returns>Return the Count of lines</returns>
        public int GetCount(int goToLine) { return IGetCount(goToLine, false); }
        /// <summary>
        /// Internal method for goto&Count
        /// </summary>
        /// <param name="goToLine">Goto Line number</param>
        /// <param name="stopWhenLine">Stop when found the selected line number</param>
        /// <returns>Return the Count of lines</returns>
        int IGetCount(int goToLine, bool stopWhenLine)
        {
            _Base.Seek(0, SeekOrigin.Begin);
            _CurrentPosition = 0;
            _CurrentLine = 0;
            _Index = 0;
            _Read = 0;

            long savePosition = _Base.Length;

            do
            {
                if (_CurrentLine == goToLine)
                {
                    savePosition = _CurrentPosition;
                    if (stopWhenLine) return _CurrentLine;
                }
            }
            while (ReadLine() != null);

            // GoToPosition

            int count = _CurrentLine;

            _CurrentLine = goToLine;
            _Base.Seek(savePosition, SeekOrigin.Begin);

            return count;
        }
        /// <summary>
        /// Read Line
        /// </summary>
        /// <returns></returns>
        public string ReadLine()
        {
            bool found = false;

            StringBuilder sb = new StringBuilder();
            while (!found)
            {
                if (_Read <= 0)
                {
                    // Read next block
                    _Index = 0;
                    _Read = _Base.Read(_Bff, 0, BufferLength);
                    if (_Read == 0)
                    {
                        if (sb.Length > 0) break;
                        return null;
                    }
                }

                for (int max = _Index + _Read; _Index < max; )
                {
                    char ch = (char)_Bff[_Index];
                    _Read--; _Index++;
                    _CurrentPosition++;

                    if (ch == '\0' || ch == '\n')
                    {
                        found = true;
                        break;
                    }
                    else if (ch == '\r') continue;
                    else sb.Append(ch);
                }
            }

            _CurrentLine++;
            return sb.ToString();
        }
        /// <summary>
        /// Free resources
        /// </summary>
        public void Dispose()
        {
            if (_Base != null)
            {
                _Base.Close();
                _Base.Dispose();
                _Base = null;
            }
        }
    }

Use:

 using (StreamLineReader st = new StreamLineReader(File.OpenRead("E:\\log.txt")))
        {
            bool ok = st.GoToLine(1);
            int count= st.GetCount(0);

            string w0 = st.ReadLine();
            string w1 = st.ReadLine();
            string w2 = st.ReadLine();
            string w3 = st.ReadLine();
        }
like image 28
Fernando Diaz Toledano Avatar answered Sep 30 '22 20:09

Fernando Diaz Toledano