FileStream StreamReader problem in C #

I am testing how the FileStream and StreamReader classes work. Through the console application. I am trying to enter a file and read the lines and print them to the console.

I was able to do this with a while loop, but I want to try it with a foreach loop.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace testing { public class Program { public static void Main(string[] args) { string file = @"C:\Temp\New Folder\New Text Document.txt"; using(FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { using(StreamReader sr = new StreamReader(fs)) { foreach(string line in file) { Console.WriteLine(line); } } } } } } 

The error I keep getting for this is: I cannot convert the type 'char' to 'string'

The while loop that works is as follows:

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

I will probably miss something really basic, but I do not see it.

+20
c #
Nov 13 '08 at 8:38
source share
10 answers

To read all the lines in New Text Document.txt:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace testing { public class Program { public static void Main(string[] args) { string file = @"C:\Temp\New Folder\New Text Document.txt"; using(FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { using(StreamReader sr = new StreamReader(fs)) { while(!sr.EndOfStream) { Console.WriteLine(sr.ReadLine()); } } } } } } 
+22
Nov 13 '08 at 8:47
source share

If you want to read the file line by line through foreach (with the possibility of reuse), consider the following iterator block:

  public static IEnumerable<string> ReadLines(string path) { using (StreamReader reader = File.OpenText(path)) { string line; while ((line = reader.ReadLine()) != null) { yield return line; } } } 

Note that this is lazily evaluated - there is no buffering that you would associate with File.ReadAllLines() . The foreach syntax guarantees the correctness of the Dispose() d iterator, even for exceptions, closing the file:

 foreach(string line in ReadLines(file)) { Console.WriteLine(line); } 



(this bit is added only for interest ...)

Another advantage of this type of abstraction is that it plays perfectly with LINQ - that is, using this approach it is easy to make transforms / filters, etc.

  DateTime minDate = new DateTime(2000,1,1); var query = from line in ReadLines(file) let tokens = line.Split('\t') let person = new { Forname = tokens[0], Surname = tokens[1], DoB = DateTime.Parse(tokens[2]) } where person.DoB >= minDate select person; foreach (var person in query) { Console.WriteLine("{0}, {1}: born {2}", person.Surname, person.Forname, person.DoB); } 

And again, everyone evaluates lazily (without buffering).

+34
Nov 13 '08 at 8:46
source share

I have a LineReader class in my MiscUtil project. It is a little more general than the solutions proposed here, mainly in terms of how to create it:

  • From a function that returns a stream, in which case it will use UTF-8
  • From stream return function and encoding
  • From a function that returns a text reader
  • On behalf of the file, in this case it will use UTF-8
  • On behalf of the file and encoding

The class "owns" any resources that it uses and closes them accordingly. However, he does this without implementing IDisposable . That is why instead of a stream or a reader, he takes Func<Stream> and Func<TextReader> - he should be able to postpone the hole until he needs it. This is the iterator itself (which is automatically located in the foreach ), which closes the resource.

As Mark noted, this works well in LINQ. One example I would like to give:

 var errors = from file in Directory.GetFiles(logDirectory, "*.log") from line in new LineReader(file) select new LogEntry(line) into entry where entry.Severity == Severity.Error select entry; 

This will cause all errors to flow from a number of log files, opening and closing as they occur. In combination with Push LINQ you can do all kinds of nice things :)

This is not a particularly "complicated" class, but it is really convenient. Here is the full source, for convenience, if you do not want to download MiscUtil. Source code license here .

 using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; namespace MiscUtil.IO { /// <summary> /// Reads a data source line by line. The source can be a file, a stream, /// or a text reader. In any case, the source is only opened when the /// enumerator is fetched, and is closed when the iterator is disposed. /// </summary> public sealed class LineReader : IEnumerable<string> { /// <summary> /// Means of creating a TextReader to read from. /// </summary> readonly Func<TextReader> dataSource; /// <summary> /// Creates a LineReader from a stream source. The delegate is only /// called when the enumerator is fetched. UTF-8 is used to decode /// the stream into text. /// </summary> /// <param name="streamSource">Data source</param> public LineReader(Func<Stream> streamSource) : this(streamSource, Encoding.UTF8) { } /// <summary> /// Creates a LineReader from a stream source. The delegate is only /// called when the enumerator is fetched. /// </summary> /// <param name="streamSource">Data source</param> /// <param name="encoding">Encoding to use to decode the stream /// into text</param> public LineReader(Func<Stream> streamSource, Encoding encoding) : this(() => new StreamReader(streamSource(), encoding)) { } /// <summary> /// Creates a LineReader from a filename. The file is only opened /// (or even checked for existence) when the enumerator is fetched. /// UTF8 is used to decode the file into text. /// </summary> /// <param name="filename">File to read from</param> public LineReader(string filename) : this(filename, Encoding.UTF8) { } /// <summary> /// Creates a LineReader from a filename. The file is only opened /// (or even checked for existence) when the enumerator is fetched. /// </summary> /// <param name="filename">File to read from</param> /// <param name="encoding">Encoding to use to decode the file /// into text</param> public LineReader(string filename, Encoding encoding) : this(() => new StreamReader(filename, encoding)) { } /// <summary> /// Creates a LineReader from a TextReader source. The delegate /// is only called when the enumerator is fetched /// </summary> /// <param name="dataSource">Data source</param> public LineReader(Func<TextReader> dataSource) { this.dataSource = dataSource; } /// <summary> /// Enumerates the data source line by line. /// </summary> public IEnumerator<string> GetEnumerator() { using (TextReader reader = dataSource()) { string line; while ((line = reader.ReadLine()) != null) { yield return line; } } } /// <summary> /// Enumerates the data source line by line. /// </summary> IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } 
+22
Nov 13 '08 at 9:13
source share

The problem is this:

 foreach(string line in file) { Console.WriteLine(line); } 

Because the "file" is a string, and the string implements IEnumerable. But this enumerator returns "char" and "char" cannot be force converted to a string.

You should use a while loop, as you said.

+3
Nov 13 '08 at 8:41
source share

A little more elegant is the following ...

 using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) { using (var streamReader = new StreamReader(fileStream)) { while (!streamReader.EndOfStream) { yield return reader.ReadLine(); } } } 
+2
Dec 07 '10 at 4:56
source share

I like homework;)

You repeat the file name (string), which gives you one character at a time. Just use the while approach, which uses sr.ReadLine () correctly.

+1
Nov 13 '08 at 8:44
source share

Instead of using StreamReader , and then trying to find the strings inside the String file variable, you can simply use File.ReadAllLines :

 string[] lines = File.ReadAllLines(file); foreach(string line in lines) Console.WriteLine(line); 
+1
Nov 13 '08 at 8:45
source share

You list a string, and when you do this, you take one char at a time.

Are you sure this is what you want?

 foreach(string line in file) 
0
Nov 13 '08 at 8:42
source share

The simplest (not memory efficient) approach to iterating over each line in a file

 foreach (string line in File.ReadAllLines(file)) { .. } 
0
Nov 13 '08 at 8:46
source share

I assume you want something like this:

 using ( FileStream fileStream = new FileStream( file, FileMode.Open, FileAccess.Read ) ) { using ( StreamReader streamReader = new StreamReader( fileStream ) ) { string line = ""; while ( null != ( line = streamReader.ReadLine() ) ) { Console.WriteLine( line ); } } } 
0
Nov 13 '08 at 8:47
source share



All Articles