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
。 这就是为什么它需要Func
和Func
而不是流或读者直接 – 它需要能够推迟开放直到它需要它。 它是迭代器本身(由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 ); } } }