0

I have found hundreds of answers reading line by line, but not one where you can specify the line you want to read. Say that my file looks like this:

A
B
C
D
E

and i want to read line #3 (in this case actually line 2 i guess - lets say I want the "C").

How do i do that?

I can of course make a loop and stop at the selected row, but isn't there a better (less ugly) way of doing this? Like MyStreamReader.Row(2).Read or something similar?

Lucas
  • 3,376
  • 6
  • 31
  • 46
gubbfett
  • 2,157
  • 6
  • 32
  • 54
  • Unless you have a file that contains structures to help you locate lines (such that it's no longer really a file with lines in the conventional sense), every byte (or pair of bytes) has to be inspected to locate line endings. I'm not sure what optimization you're assuming `Row(2)` would be able to make. – Damien_The_Unbeliever Jul 15 '13 at 13:22
  • This question's already be answered http://stackoverflow.com/questions/5404267/streamreader-and-seeking You can't seek or modify the stream position with StreamReader because of different encoding formats. If your lines are a fixed length you can use a FileStream and seek or set the position with (LineLength * NumberOfLines) – Louis Ricci Jul 15 '13 at 13:26
  • @Damien_The_Unbeliever - i wasn't really looking for an optimization, just a way to write it a little less ulgy than a loop and a stop index. I was looking for an built in function to do this - and talking about optimization; the built in functions are also usually a little faster? – gubbfett Jul 15 '13 at 13:36
  • @LastCoder - Good idea, but in this case the lines in the file will not have a fixed length. – gubbfett Jul 15 '13 at 13:37

1 Answers1

1

The most efficient approach is streaming the lines and count each line. With File.ReadAllLines you must wait for the whole array of strings be returned before you can access the array.

An easy approach is using File.ReadLines which works similar to a stream reader:

Dim thirdLine = File.ReadLines(path).ElementAtOrDefault(2)

Enumerable.ElementAtOrDefault returns Nothing if the specified index is too large. So you can check it in this way:

If thirdLine IsNot Nothing Then
    Console.WriteLine("Third line: " & thirdLine)
Else
    Console.WriteLine("The file doesn't contain 3 lines")
End If
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • This method still reads the entire file, does it not? Is it smart enough to know to stop streaming when you have read the line at index 2, for example? – Chris Dunaway Jul 15 '13 at 13:49
  • @ChrisDunaway: Yes, it stops as soon as the index is reached since it's deferred executed. It's the same as a stream-reader with a counter variable, just more readable. However, if you would prepend a `ToList` or `ToArray` before `ElementAtOrdefault` the entire file would be read into memory. – Tim Schmelter Jul 15 '13 at 13:56
  • This will definitely parse the whole file into a string array. As long as the file isn't too large it will work, but looping reading 1 line at a time would be more optimal. unless you will need the other lines of the file eventually anyways. – Louis Ricci Jul 15 '13 at 13:58
  • @LastCoder: No, thanks to Linq's deferred execution it will stream the lines until `ElementAtOrDefault` reaches the desired index. Then this line will be returned. I've tested it with a file with 1.000.000 lines. `File.ReadAllLines(path)[0]` needed `00:00:00.5661544` whereas `File.ReadLines(path).ElementAtOrDefault(0)` needed just `00:00:00.0003953`. – Tim Schmelter Jul 15 '13 at 14:27
  • @Tim Schmelter - So .NET will use a different code path for ReadAllLines depending on if it's immediately indexed or a IEnumerable helper function is called on the result. If the CLR is optimizing the latter case you'd think it would optimize the first more obvious case too. – Louis Ricci Jul 15 '13 at 14:36
  • @LastCoder: I assume that you've overlooked that i'm comparing `FileReadAllLines` and `File.ReadLines` which do completely different things behind the scenes. `FileReadAllLInes` reads all lines of a file into memory and creates a `string[]`. `ReadLines` is executed lazily, that means it does nothing alone (have a look at [iterators](http://msdn.microsoft.com/en-us/library/65zzykke(v=vs.100).aspx) and the `yield` keyowrd in C#). If you append `Take(10)` it would just read the first ten lines, with `ElementAt/OrDefault` it skips all but the specified index and returns as soon as it is reached. – Tim Schmelter Jul 15 '13 at 14:43
  • @TimSchmelter - ReadLins vs ReadAllLins would make all the difference. Yes I did misread it. – Louis Ricci Jul 15 '13 at 15:00