C#中的FileStream StreamReader问题

我正在测试类FileStream和StreamReader如何工作。 通过控制台应用程序。 我正在尝试进入文件并读取行并在控制台上打印它们。

我已经能够使用while循环来完成它,但我想尝试使用foreach循环。

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); } } } } } } 

我一直在犯的错误是:无法将’char’类型转换为’string’

while循环确实有效,如下所示:

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

我可能忽略了一些非常基本的东西,但我看不到它。

要读取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()); } } } } } } 

如果要通过foreach(以可重用的方式)逐行读取文件,请考虑以下迭代器块:

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

请注意,这是懒惰的评估 – 没有与File.ReadAllLines()关联的缓冲。 foreach语法将确保迭代器是Dispose() d,即使是exception,关闭文件:

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

(这个位只是为了兴趣…)

这种抽象的另一个优点是它可以很好地与LINQ一起使用 – 即使用这种方法很容易进行转换/过滤等:

  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); } 

再一次,所有人都懒洋洋地评估(没有缓冲)。

我的MiscUtil项目中有一个LineReader类。 它比这里给出的解决方案略胜一筹,主要是根据你构建它的方式:

  • 从返回流的函数,在这种情况下,它将使用UTF-8
  • 从返回流的函数和编码
  • 从返回文本阅读器的函数
  • 仅从文件名,在这种情况下,它将使用UTF-8
  • 从文件名和编码

该类“拥有”它使用的任何资源,并适当地关闭它们。 但是,如果不实现IDisposable本身,它就IDisposable 。 这就是为什么它需要FuncFunc而不是流或读者直接 – 它需要能够推迟开放直到它需要它。 它是迭代器本身(由foreach循环自动处理),它关闭资源。

正如Marc所指出的,这在LINQ中非常有效。 我想给出的一个例子是:

 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; 

这将从一大堆日志文件中流出所有错误,打开和关闭。 结合Push LINQ,你可以做各种好东西:)

这不是一个特别“棘手”的课程,但它真的很方便。 这里是完整的源代码,为方便起见,如果您不想下载MiscUtil。 源代码的许可证在这里 。

 using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; namespace MiscUtil.IO { ///  /// 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. ///  public sealed class LineReader : IEnumerable { ///  /// Means of creating a TextReader to read from. ///  readonly Func dataSource; ///  /// 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. ///  /// Data source public LineReader(Func streamSource) : this(streamSource, Encoding.UTF8) { } ///  /// Creates a LineReader from a stream source. The delegate is only /// called when the enumerator is fetched. ///  /// Data source /// Encoding to use to decode the stream /// into text public LineReader(Func streamSource, Encoding encoding) : this(() => new StreamReader(streamSource(), encoding)) { } ///  /// 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. ///  /// File to read from public LineReader(string filename) : this(filename, Encoding.UTF8) { } ///  /// Creates a LineReader from a filename. The file is only opened /// (or even checked for existence) when the enumerator is fetched. ///  /// File to read from /// Encoding to use to decode the file /// into text public LineReader(string filename, Encoding encoding) : this(() => new StreamReader(filename, encoding)) { } ///  /// Creates a LineReader from a TextReader source. The delegate /// is only called when the enumerator is fetched ///  /// Data source public LineReader(Func dataSource) { this.dataSource = dataSource; } ///  /// Enumerates the data source line by line. ///  public IEnumerator GetEnumerator() { using (TextReader reader = dataSource()) { string line; while ((line = reader.ReadLine()) != null) { yield return line; } } } ///  /// Enumerates the data source line by line. ///  IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } 

问题在于:

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

因为“文件”是字符串,而字符串实现IEnumerable。 但是这个枚举器返回“char”并且“char”不能被隐含地转换为字符串。

你应该使用while循环。

以下稍微优雅一点……

 using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) { using (var streamReader = new StreamReader(fileStream)) { while (!streamReader.EndOfStream) { yield return reader.ReadLine(); } } } 

看起来像我的作业;)

你正在迭代文件名(一个字符串)本身,它一次给你一个字符。 只需使用正确使用sr.ReadLine()的while方法。

您可以简单地使用File.ReadAllLines ,而不是使用StreamReader ,然后尝试在String file变量中查找行。

 string[] lines = File.ReadAllLines(file); foreach(string line in lines) Console.WriteLine(line); 

您正在枚举一个字符串,当您这样做时,您将获取一个字符串。

你确定这是你想要的吗?

 foreach(string line in file) 

迭代文件中每一行的简单(非内存效率)方法是

 foreach (string line in File.ReadAllLines(file)) { .. } 

我认为你想要这样的东西:

 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 ); } } }