C#中的FileStream StreamReader问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/286533/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
FileStream StreamReader problem in C#
提问by Vordreller
I'm testing how the classes FileStream and StreamReader work togheter. Via a Console application. I'm trying to go in a file and read the lines and print them on the console.
我正在测试类 FileStream 和 StreamReader 如何一起工作。通过控制台应用程序。我试图进入一个文件并读取这些行并在控制台上打印它们。
I've been able to do it with a while-loop, but I want to try it with a foreach loop.
我已经能够用 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);
}
}
}
}
}
}
The error I keep getting for this is: Cannot convert type 'char' to 'string'
我不断收到的错误是:无法将类型“char”转换为“string”
The while loop, which does work, looks like this:
确实有效的 while 循环如下所示:
while((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
I'm probably overlooking something really basic, but I can't see it.
我可能忽略了一些非常基本的东西,但我看不到它。
采纳答案by Aleksandar
To read all lines in New Text Document.txt:
阅读 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());
}
}
}
}
}
}
回答by TcKs
The problem is in:
问题在于:
foreach(string line in file)
{
Console.WriteLine(line);
}
Its because the "file" is string, and string implements IEnumerable. But this enumerator returns "char" and "char" can not be implictly converted to string.
因为“文件”是字符串,而字符串实现了IEnumerable。但是这个枚举器返回“char”并且“char”不能隐式转换为字符串。
You should use the while loop, as you sayd.
正如您所说,您应该使用 while 循环。
回答by Mikael S?derstr?m
You are enumerating a string, and when you do that, you take one char at the time.
您正在枚举一个字符串,当您这样做时,您会在当时获取一个字符。
Are you sure this is what you want?
你确定这是你想要的吗?
foreach(string line in file)
回答by VVS
Looks like homework to me ;)
对我来说看起来像家庭作业;)
You're iterating over the filename (a string) itself which gives you one character at a time. Just use the while approach that correctly uses sr.ReadLine().
您正在迭代文件名(一个字符串)本身,一次给您一个字符。只需使用正确使用 sr.ReadLine() 的 while 方法。
回答by gimel
Instead of using a StreamReader
and then trying to find lines inside the String file
variable, you can simply use File.ReadAllLines
:
而不是使用 aStreamReader
然后尝试在String file
变量中查找行,您可以简单地使用File.ReadAllLines
:
string[] lines = File.ReadAllLines(file);
foreach(string line in lines)
Console.WriteLine(line);
回答by VVS
A simplistic (not memory efficient) approach of iterating every line in a file is
迭代文件中每一行的简单(内存效率不高)方法是
foreach (string line in File.ReadAllLines(file))
{
..
}
回答by Marc Gravell
If you want to read a file line-by-line via foreach (in a reusable fashion), consider the following iterator block:
如果您想通过 foreach 逐行读取文件(以可重用的方式),请考虑以下迭代器块:
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 this is lazily evaluated - there is none of the buffering that you would associate with File.ReadAllLines()
. The foreach
syntax will ensure that the iterator is Dispose()
d correctly even for exceptions, closing the file:
请注意,这是懒惰的评估 - 没有任何缓冲可以与File.ReadAllLines()
. 该foreach
语法将确保迭代器Dispose()
甚至例外正确Ð,关闭文件:
foreach(string line in ReadLines(file))
{
Console.WriteLine(line);
}
(this bit is added just for interest...)
(添加这一点只是为了兴趣......)
Another advantage of this type of abstraction is that it plays beautifully with LINQ - i.e. it is easy to do transformations / filters etc with this approach:
这种类型的抽象的另一个优点是它与 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);
}
And again, all evaluated lazily (no buffering).
再次,所有评估都是懒惰的(无缓冲)。
回答by RichS
I presume 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 );
}
}
}
回答by Jon Skeet
I have a LineReader
class in my MiscUtilproject. It's slightly more general than the solutions given here, mostly in terms of the way you can construct it:
LineReader
我的MiscUtil项目中有一个类。它比此处给出的解决方案更通用,主要是在构建它的方式方面:
- From a function returning a stream, in which case it will use UTF-8
- From a function returning a stream, and an encoding
- From a function which returns a text reader
- From just a filename, in which case it will use UTF-8
- From a filename and an encoding
- 从返回流的函数中,在这种情况下它将使用 UTF-8
- 从返回流的函数和编码
- 从返回文本阅读器的函数
- 仅来自文件名,在这种情况下它将使用 UTF-8
- 从文件名和编码
The class "owns" whatever resources it uses, and closes them appropriately. However, it does this without implementing IDisposable
itself. This is why it takes Func<Stream>
and Func<TextReader>
instead of the stream or the reader directly - it needs to be able to defer the opening until it needs it. It's the iterator itself (which is automatically disposed by a foreach
loop) which closes the resource.
该类“拥有”它使用的任何资源,并适当地关闭它们。但是,它在没有实现IDisposable
自身的情况下做到了这一点。这就是为什么它需要Func<Stream>
而Func<TextReader>
不是直接使用流或读取器 - 它需要能够推迟打开直到它需要它。是迭代器本身(由foreach
循环自动处理)关闭资源。
As Marc pointed out, this works really well in LINQ. One example I like to give is:
正如 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;
This will stream all the errors from a whole bunch of log files, opening and closing as it goes. Combined with Push LINQ, you can do all kinds of nice stuff :)
这将从一大堆日志文件中流式传输所有错误,同时打开和关闭。结合 Push LINQ,你可以做各种各样的好东西:)
It's not a particularly "tricky" class, but it's really handy. Here's the full source, for convenience if you don't want to download MiscUtil. The licence for the source code is here.
这不是一个特别“棘手”的课程,但它真的很方便。这是完整的源代码,如果您不想下载 MiscUtil,为方便起见。源代码的许可证在这里。
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();
}
}
}
回答by Bob
Slightly 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();
}
}
}