90

I'm reading a file in line-by-line and I want to be able to restart the read by calling a method Rewind().

How can I manipulate my System.IO.StreamReader and/or its underlying System.IO.FileStream to start over with reading the file?

I got the clever idea to use FileStream.Seek(long, SeekOffset) to move around the file, but it has no effect the enclosing System.IO.StreamReader. I could Close() and reassign both the stream and the reader referecnes, but I'm hoping there's a better way.

leppie
  • 115,091
  • 17
  • 196
  • 297
Chris
  • 3,438
  • 5
  • 25
  • 27

8 Answers8

132

You need to seek on the stream, like you did, then call DiscardBufferedData on the StreamReader. Documentation here:

Edit: Adding code example:

Stream s = new MemoryStream();
StreamReader sr = new StreamReader(s);
// later... after we read stuff
s.Position = 0;
sr.DiscardBufferedData();        // reader now reading from position 0
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
  • 8
    Be careful when doing this on any files that are not simple ANSI text files (or rather any files that have an encoding pre-amble). See my answer below for more detail. – Eamon Jul 16 '15 at 02:05
  • You are forgetting the using clause / IDisposable pattern. New to .NET 4.0 is StreamReader(stream, Encoding.UTF8, true, 1024, true) and new StreamWriter(stream, Encoding.UTF8, 1024, true) overcome the broken IDisposable pattern (closing StreamReader/Writer closes underlying stream...even M$ doesn't always get it right, at least at first). If you need to read from a stream more than once, have two or more StreamReaders, each in their own using clauses (not nested inside each other). Likewise with StreamWriter. It should be very rare that you call DiscardBufferedData, per documentation. – TamusJRoyce Jun 30 '16 at 21:14
59

I use this method:

System.IO.StreamReader reader = new System.IO.StreamReader("file.txt")
//end of reading
reader.DiscardBufferedData();
reader.BaseStream.Seek(0, System.IO.SeekOrigin.Begin); 
31

Amy's answer will work on some files but depending on the underlying stream's encoding, you may get unexpected results.

For example if the stream is UTF-8 and has a preamble, then the StreamReader will use this to detect the encoding and then switch off some internal flags that tells it to detect the encoding and check the preamble. If you reset the stream's position to the beginning, the stream reader will now consume the preamble again but it will include it in the output the second time. There is no public methods to reset this encoding and preamble state so the safest thing to do if you need to "rewind" a stream reader is to seek the underlying stream to the beginning (or set position) as shown and create a new StreamReader, just calling DiscardBufferedData() on the StreamReader will not be sufficient.

riQQ
  • 9,878
  • 7
  • 49
  • 66
Eamon
  • 1,829
  • 1
  • 20
  • 21
19

This is all good if the BaseStream can actually be set Position property to 0.

If you cannot (example would be a HttpWebResponse stream) then a good option would be to copy the stream to a MemoryStream...there you can set Position to 0 and restart the Stream as much as you want.

TheBoyan
  • 6,802
  • 3
  • 45
  • 61
2

I had created a simple method, that you can use to reset the StreamReader.

using (var reader = StreamReader(filePath))
{
   reader.ReadLine();
   reader.ReadLine();
   ResetReader(reader);
}


private static void ResetReader(StreamReader reader)

 {
     reader.DiscardBufferedData();
     reader.BaseStream.Seek(0, SeekOrigin.Begin);
 }
saad bin sami
  • 364
  • 4
  • 11
1

The question is looking for some kind of StreamReader.Rewind() method. You are looking for StreamReader.BaseStream.Position = 0; which sets the reader back to the beginning so it can be read again.

StreamReader sr = new StreamReader("H:/kate/rani.txt");

Console.WriteLine(sr.ReadToEnd());

sr.BaseStream.Position = 0;

Console.WriteLine("----------------------------------");

while (!sr.EndOfStream)
{
    Console.WriteLine(sr.ReadLine());
}
ono2012
  • 4,967
  • 2
  • 33
  • 42
Md Shahriar
  • 2,072
  • 22
  • 11
  • 1
    This did not work well for me. The HTML file being read in was displayed twice after the first line was read and then the suggested reset was used. Perhaps it works for some cases, but not for mine. – JohnH Nov 10 '16 at 16:06
  • 4
    Works well if i use sr.BaseStream.Position = 0; sr.DiscardBufferedData(); – Chashitsu Jan 07 '19 at 15:42
1
public long ReadList(string fileName, Action<string> action,long position=0)
{
  if (!File.Exists(fileName)) return 0;

  using (var reader = new StreamReader(File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),System.Text.Encoding.Unicode))
  {
    if (position > 0)reader.BaseStream.Position = position;

     while (!reader.EndOfStream)
     {
       action(reader.ReadLine());
     }
     return reader.BaseStream.Position;
   }
}
thanksd
  • 54,176
  • 22
  • 157
  • 150
vyv
  • 19
  • 1
0
public static void removeDuplicatedLinesBigFile2(string inFile, string outFile)
{
    int counter1 = 0, counter2 = 0;
    string line1, line2;
    bool band = false;

    // Read the file and display it line by line.  
    System.IO.StreamReader fileIN1 = new System.IO.StreamReader(inFile);
    System.IO.StreamReader fileIN2 = new System.IO.StreamReader(inFile);
    System.IO.StreamWriter fileOut = new System.IO.StreamWriter(outFile);

    while ((line1 = fileIN1.ReadLine()) != null)
    {
        //band = false;
        int counter = 0;
        fileIN2.BaseStream.Position = 0;
        fileIN2.DiscardBufferedData();

        while ((line2 = fileIN2.ReadLine()) != null)
        {                   
            if (line1.Equals(line2))
                counter++;

            if (counter > 1)
                break;
        }

        fileOut.WriteLine(line1);
        counter1++;
    }

    fileIN1.Close();
    fileIN2.Close();
    Console.WriteLine("Total Text Rows Copied: {0}", counter1);
}
  • This method let you process a text file to create a new one and let you void copy repeated text lines. Here is a example of use DiscardBufferedData() after BaseStream.Position = 0; – Mauricio Kenny Sep 28 '20 at 02:58