12

I have a simple program to read a file using the StreamReader and process it line by line. But the file I am reading may sometimes locate in a network folder. I came across while doing some testing with such a file, that if the network connection lost at some point while I am reading, it'll stay in the same line again and again looping in an infinite loop by resulting the same line as the result from stream.ReadLine().

Is there a way I can find when the fileHandle is not available from the stream itself? I was expecting a FileNotAvailableException kind of an exception would fire when the filehandle is lost from the StreamReader.

Here's my code snippet...

        string file = @"Z://1601120903.csv"; //Network file
        string line;
        StringBuilder stb = new StringBuilder();      
        StreamReader stream = new StreamReader(file, Encoding.UTF8, true, 1048576);
        do
        {
            line = stream.ReadLine();
            // Do some work here
        } while (line != "");
Asanka
  • 539
  • 3
  • 6
  • 14
  • 3
    StreamReader cannot "lose" the handle. If it in fact can't use cached data and has to hit the network to obtain file content then the underlying ReadFile() call will fail and you'd get a System.IOException. Which reports "Network error", one of the many things that can go wrong when reading files. You'd catch the exception and tell the user about it so he can do whatever is necessary to correct the problem. – Hans Passant Feb 10 '16 at 15:14
  • 1
    I'm pondering whether to close this because there is a bug here that is not visible in this code. The code shown here must be a simplified version of the real code. – usr Feb 10 '16 at 15:19
  • @HansPassant, yes I also initially thought the same thing. But if you execute this line of code,you'll experience the issue. It doesn't throw an exception as it keeps throwing me the same line indefinitely. – Asanka Feb 10 '16 at 15:23
  • @usr, You can experience the issue if you execute this code while you are reading from a file. At the middle of the file close the connection to the file share. – Asanka Feb 10 '16 at 15:25
  • 1
    You are supposed to take care of the error handling. That your code snippet does not have any is a simple explanation for what you are complaining about. – Hans Passant Feb 10 '16 at 15:28
  • @HansPassant Yes I understand that. Wrapping this with a try catch block will not fix my problem, will it? – Asanka Feb 10 '16 at 15:31
  • 3
    You can certainly wrap it wrong. This is **endlessly** easier if you show your real code so we don't constantly have to guess at it. – Hans Passant Feb 10 '16 at 15:35

6 Answers6

19

Compare with null not with empty string:

https://msdn.microsoft.com/en-us/library/system.io.streamreader.readline(v=vs.110).aspx

Return Value Type: System.String The next line from the input stream, or null if the end of the input stream is reached.

    do
    {
        line = stream.ReadLine();
        // Do some work here
    } while (line != null);

A better approach, however, is to let .Net do the work (line by line file reading) for you and drop all readers:

  foreach (String line in File.ReadLines(file)) {
    // Do some work here
  }
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • 1
    would it improve code to use something like `string.IsNullOrEmpty(line)`? – terbubbs Feb 10 '16 at 15:06
  • 5
    @terbubbs: no, an *empty string* can well appear in the middle of the file – Dmitry Bychenko Feb 10 '16 at 15:07
  • But the issue is not with the checking of null or empty. After the filehandle is dropped due to network issues, stream.ReadLine() outputs the same line again and again to variable line. It'll never be empty or null – Asanka Feb 10 '16 at 15:10
  • 1
    @DmitryBychenko, File.ReadLines will read all the lines into memory, while stream.ReadLine will read line by line in a sequential manner without affecting the memory. Reading all the lines into memory is not an option to me as the file itself may be quite large – Asanka Feb 10 '16 at 15:20
  • 3
    @Asanka: please note, that I've put `File.ReadLines` not `File.ReadAllLines`; and `File.ReadLines` doesn't load all the lines into memory – Dmitry Bychenko Feb 10 '16 at 15:22
  • @DmitryBychenko.. I see. Thanks for the info. I didn't come across this approach before. I'll give it a go and let you know. – Asanka Feb 10 '16 at 15:37
  • @Asanka, it seems that `@"Z:\1601120903.csv"` is the right file name you want to work with; – Dmitry Bychenko Feb 10 '16 at 15:39
8

Correct approach 1 (EndOfStream) :

using(StreamReader sr = new StreamReader(...)) {
    while(!sr.EndOfStream) {
        string line = sr.ReadLine();
        Console.WriteLine(line);
    }
}

Correct approach 2 (Peek)

using(StreamReader sr = new StreamReader(...)) {
    while(sr.Peek() >= 0) {
        string line = sr.ReadLine();
    }
}

Note: that it is incorrect to threat an empty string as end of file.

if the network connection lost at some point while I am reading, it'll stay in the same line again and again looping in an infinite loop by resulting the same line as the result from stream.ReadLine()

I've checked this scenario right now - the System.IO.IOException ("The network path was not found."} should be thrown in this case.

Wrapping this with a try catch block will not fix my problem, will it?

In this case you can break the reading as follows:

string line;
do {
    try {
        line = sr.ReadLine();
        // Do some work here
    }
    catch(System.IO.IOException) {
        break; 
    }
} while(line != null);
DmitryG
  • 17,677
  • 1
  • 30
  • 53
  • I checked those two properties. After the filehandle is dropped due to network issues, both these conditions will return true and stream.ReadLine() outputs the same line again and again to variable line. – Asanka Feb 10 '16 at 15:15
  • @Asanka I've updated my answers with some results of real-life testing. – DmitryG Feb 10 '16 at 15:34
  • 1
    Strange. It didn't throw me any exception. Did you drop the connection while you are in the middle of the file read operation? – Asanka Feb 10 '16 at 15:35
  • Yes, I've physically remove my laptop Ethernet-cable)) – DmitryG Feb 10 '16 at 15:35
  • @Asanka I also tested disconnecting the mapped drive and get an exception thrown, but only after it's read through the buffer (the buffer that OP has set to 1048576). Me thinks asanka has a CSV file with duplicate lines in it and when they drop the network connection they are just spitting out the buffer and not waiting for it to reach the end of that buffer to throw an exception. Lower the buffer to 100 and try again. – Quantic Feb 10 '16 at 16:10
  • @DmitryG, I will check with a relatively smaller buffer size then... Thanx for the info – Asanka Feb 10 '16 at 16:37
  • In my opinion you shouldn't use exception handling to control the flow of your application. – Jamie Rees Feb 10 '16 at 17:30
4

If you write it with a while-loop:

while ((line = sr.ReadLine()) != null)
{
    Console.WriteLine(line);
}

Source

Community
  • 1
  • 1
diiN__________
  • 7,393
  • 6
  • 42
  • 69
1

One more way would be to use File.ReadAllLines() and it will take care of opening file and reading all lines and closig the file and may also handle scenario when network connection is lost.

var lines = File.ReadAllLines("Z://1601120903.csv");

foreach(line in lines)
{
 // Do some work
}
Viru
  • 2,228
  • 2
  • 17
  • 28
0

Assuming the file shouldn't change while you reading it and it's not huge, you might want to consider to copy it to a temp file (locally) and then work on it without interference.

If you want to get index of the place you reached this might help: How to know position(linenumber) of a streamreader in a textfile?

Community
  • 1
  • 1
Felix Av
  • 1,254
  • 1
  • 14
  • 22
  • Copying the file locally seems like a good solution. But there might be a possibility that the file itself is quite large – Asanka Feb 10 '16 at 15:17
  • 1
    This does not address the problem that he has. He incorrectly assumes that the same line might be output repeatedly. Instead, this is a bug with his code. – usr Feb 10 '16 at 15:18
  • @Asanka as I've stated check if copying a file is possible, and if you still want to keep track of the current reader index you should use the solution I've added. – Felix Av Feb 10 '16 at 15:21
0

If your stream is a NetworkStream, the ReadLine method will expect more content from the stream, if it reached at end, indefinitely. I think, and according to the StreamReader documentation, it is designed to work only with local file streams. In this case, you can read bytes directly from the NetworkStream.

https://learn.microsoft.com/pt-br/dotnet/api/system.net.sockets.networkstream.read?view=netcore-3.1#System_Net_Sockets_NetworkStream_Read_System_Span_System_Byte__

otaviodecampos
  • 1,586
  • 16
  • 10