Hi all,
Ive been trying to find a seek method for StreamReader. I want to be able to reverse the file buffer by a couple characters at a time.

How I have everything set up.

private void SortThread()
        {
            string[] lines;

            string  line,
                    totalLine = null;

            char[] fileInput = new char[1001];

            TextReader tr;
            

            tr = new StreamReader("C:\\Users\\Anon\\Desktop\\test.txt");
            line = null;
           
            do
            {
                try
                {
                    //read 1k characters at a time
                    tr.Read(fileInput, 0, 1000);


                    //turn them to an array of lines
                    line = new string(fileInput);
                    lines = line.Split('\n');
                    

                    //go backwards in the buffer and remove last line
                    int reverse = lines[lines.Count() - 1].Length;
                    lines[lines.Count() - 1] = null;
                    //buffer goes back by the amount in reverse
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }

            } while (line != null);

            tr.Close();
        }

The reason for this is because its reading 1000 characters at a time (for managing the lines array later; faster) it will have the last line incomplete. So I remove the last line and reverse back by the incomplete size. So when it reads again it will start at the beginning of the last line that was incomplete. Also is it going to throw an exception when I try to read 1000 char and theres less than 1k in the file?

Recommended Answers

All 8 Replies

Use a FileStream instead of a StreamReader as it has a Seek method.

And no, Read will stop at the end of the file. It also has a return value that tells the number of characters read.

Use Peek() method to check end of of stream.

StreamReader sr = new StreamReader(file);
 char [] ar = new char[1000];
 
 StringBuilder sb = new StringBuilder();
 while (sr.Peek() !=-1)
   {
      sr.Read(ar, 0, 1000);
      sb.Append(ar);
   }
 string []lines=sb.ToString().Split('\n');

@Momerath
The return for Read is the next character or -1 not the index. I will look into FileStream instead.

@Momerath
The return for Read is the next character or -1 not the index. I will look into FileStream instead.

Not the one you are using, it returns the number of bytes read.

Even with FileStream, the seek has no effect when combined with StreamReader. This is ridiculously complex for something that is a basic necessity. Im using StreamReader with it because of the byte to string conversion isnt simple. I could do it though a loop. But this app needs to be completely optimized.


edit: I got it to seek with this

reader.DiscardBufferedData();
                file.Seek((-lastLineLength), SeekOrigin.Current);
                reader.BaseStream.Seek((-lastLineLength), SeekOrigin.Current);

if its not exactly like that, it wont change the position. Works fine the first time and seeks only once. But then seeks twice the second time (wtf) its run.

Depending on the encoding the the text file you'll be dropping characters when you seek (which is why you should use a byte stream reader). If you are using UTF16, you need to seek back (at least) 2 positions per character since they take (at least) 2 bytes.

I'm not sure why you think what you are doing will be faster. The system is going to read 4k bytes each time it has to go to the disk (it already does buffering) so you playing around with a 1k buffer isn't going to help at all.

commented: confirmed. +9

The 1k is just as an example, once its running as its suppose to im going to increase the size. But your answer doesn't rationalize how im getting erratic seek results with the example code I posted above.

Performing a seek on the BaseStream of a StreamReader doesn't seem to have any affect unless its internal buffer is empty. As I said before, StreamReader doesn't really read from the stream, it reads from it's buffer which it refills when you reach the end of it. I don't know of any way to move the 'pointer' within this buffer other than having a buffer smaller than the amount of data you are reading.

You are still better off letting the system handle the buffering, it's good at it.

Test I just ran gave me this:
Reading 10,000,000 lines took 5533 milliseconds

If you really must do this, you're probably going to have to write your own version of StreamReader. Use TextReader as the base (as does StreamReader) and build your own buffering system in it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.