92

There exists a File.ReadAllLines but not a Stream.ReadAllLines.

using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Test_Resources.Resources.Accounts.txt")) using (StreamReader reader = new StreamReader(stream)) { // Would prefer string[] result = reader.ReadAllLines(); string result = reader.ReadToEnd(); } 

Does there exist a way to do this or do I have to manually loop through the file line by line?

2
  • How about reader.ReadToEnd().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); ? Commented Nov 9, 2012 at 17:25
  • @LB, I was considering the same. But it seems awfully inefficient. Commented Nov 9, 2012 at 17:26

6 Answers 6

144

You can write a method which reads line by line, like this:

public IEnumerable<string> ReadLines(Func<Stream> streamProvider, Encoding encoding) { using (var stream = streamProvider()) using (var reader = new StreamReader(stream, encoding)) { string line; while ((line = reader.ReadLine()) != null) { yield return line; } } } 

Then call it as:

var lines = ReadLines(() => Assembly.GetExecutingAssembly() .GetManifestResourceStream(resourceName), Encoding.UTF8) .ToList(); 

The Func<> part is to cope when reading more than once, and to avoid leaving streams open unnecessarily. You could easily wrap that code up in a method, of course.

If you don't need it all in memory at once, you don't even need the ToList...

Sign up to request clarification or add additional context in comments.

13 Comments

Do you need to check !reader.EndOfStream as well or is that not necessary?
@CodeBlend: No, ReadLine returns null when it reaches the end of the stream.
@Brondahl: Yes, there's a benefit: you may not know whether the stream has ended until you try to read from it. There may be no more data to come, which is what you actually care about. (An edit here would have been inappropriate - edits shouldn't change the intention, which this one certainly would have done.) (I suspect there may be cases where StreamReader.EndOfStream will basically give the wrong result here, but I'm not going to spend time trying to prove it right now.)
@JonSkeet I think this is getting to the part of Streams that I've never really needed to understand since I've only ever used them with Files or WebRequests. If I've understood you correctly, then you're saying that there are ways of loading data into a Stream such that .EndOfStream could return false, but calling .ReadLine() would still return null. Is that right? Is the opposite also possible? That .EndOfStream could return true, but calling .ReadLine() would return actual data?
@WENDYN: And then it would be invalid for anyone with nullable reference types turned off, or using an older compiler. Aside from anything else, I'm not going to revise nearly 20,000 answers every year when a new version of C# comes out, just to see whether new features are relevant. Anyone using NRTs should be aware of how to adapt non-NRT code.
|
49

The .EndOfStream property can be used in the loop instead of checking if the next line is not null.

List<string> lines = new List<string>(); using (StreamReader reader = new StreamReader("example.txt")) { while(!reader.EndOfStream) { lines.Add(reader.ReadLine()); } } 

1 Comment

See discussion on Jon Skeet's answer. Using .EndOfStream doesn't necessarily work for all Streams. I suspect it'll be fine for "simple" streams like Files and WebRequests, though.
8

Short Answer

Yes you have to loop line by line.

Details

Here the simplest approach. It is taken from ReadAllLines, File.cs > InternalReadAllLines > ReadLine, StreamReader.cs.

You will see that the reference version handles all line terminator combinations: \r, \n, and \r\n correctly.

ReadLine does not return an extra empty string when the line terminator is \r\n, which is typical in DOS/Windows.

ReadLine also discards any text after the final delimiter.

public static String[] ReadAllLines(this TextReader reader) { String line; List<String> lines = new List<String>(); while ((line = reader.ReadLine()) != null) { lines.Add(line); } return lines.ToArray(); } 

While there are reasons to not use ReadAllLines at all, this is what the op asked.

This accepts a TextReader, not just a StreamReader. It supports a StreamReader or a StringReader.

BTW the name StreamReader is an abomination, since it does not read streams, but implements TextReader for files. In contrast a Stream: "Provides a generic view of a sequence of bytes. This is an abstract class." In other words it could be a FileStream - binary stream with possibly no applicable text encoding.

Why Use ReadLine

Text files are post-fix delimited; meaning a new-line terminates each line. Also there are 3 combinations for newlines in common use across Windows, Unix and Mac O/S. Your application may never be ported to another O/S, but it may be expected to read an external file from a foreign O/S.

Split is not equivalent to ReadLine. Split is suited best used for infix delimited strings, such as comma separated lists. It is unsuited for post-fix strings, where the delimiter may be one of three combinations. Split "sees" (parses) \r followed by \n as 2 separate delimiters and returns an empty string. It also returns any text after the final delimiter.

The StringSplitOptions.RemoveEmptyEntries option suggested in some other answers removes all empty lines, including those which were in the original input.

Thus for the input:

line1\r \r line3\r\n 

ReadLine returns 3 lines. The 2nd is empty. Split creates 4 strings. (There is an additional string after the last \n.) It then removes the 2nd and the 4th. This is not what ReadAllLines does.

2 Comments

I totally appreciate this answer, and agree it is correct. But it isn't actually an answer. It's just a comment about someone elses answer.
@JohnHenckel I am sure I don't remember 8 years ago, but I am guessing SO blocked such a long comment. This was not the last time I was verbose and non-responsive to the OP. I should have been a lawyer, but I hate it when people do that to me :) I have edited my answer to actually answer the question, with code, before ranting on the use of Split
6
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Test_Resources.Resources.Accounts.txt")) using (StreamReader reader = new StreamReader(stream)) { // Would prefer string[] result = reader.ReadAllLines(); string[] result = reader.ReadToEnd().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); } 

Comments

6

Using the following extension method:

public static class Extensions { public static IEnumerable<string> ReadAllLines(this StreamReader reader) { string line; while ((line = reader.ReadLine()) != null) { yield return line; } } } 

It's possible to get to your desired code:

using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Test_Resources.Resources.Accounts.txt")) using (StreamReader reader = new StreamReader(stream)) { string[] result = reader.ReadAllLines().ToArray(); } 

Comments

2

If you want to use StreamReader then yes, you will have to use ReadLine and loop throught the StreamReader, reading line by line.

Something like that:

string line; using (StreamReader reader = new StreamReader(stream)) { while ((line = reader.ReadLine()) != null) { Console.WriteLine(line); } } 

or try

using (StreamReader reader = new StreamReader("file.txt")) { string[] content = reader.ReadToEnd().Replace("\n","").Split('\t'); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.