簡體   English   中英

C#中的FileStream StreamReader問題

[英]FileStream StreamReader problem in C#

我正在測試類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);
}

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

如果要通過foreach(以可重用的方式)逐行讀取文件,請考慮以下迭代器塊:

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

請注意,這是懶惰的評估 - 沒有與File.ReadAllLines()關聯的緩沖。 foreach語法將確保迭代器是Dispose() d,即使是異常,關閉文件:

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

而且,所有人都懶洋洋地評估(沒有緩沖)。

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

我的MiscUtil項目中有一個LineReader類。 它比這里給出的解決方案略勝一籌,主要是根據你構建它的方式:

  • 從返回流的函數,在這種情況下,它將使用UTF-8
  • 從返回流的函數和編碼
  • 從返回文本閱讀器的函數
  • 僅從文件名,在這種情況下,它將使用UTF-8
  • 從文件名和編碼

該類“擁有”它使用的任何資源,並適當地關閉它們。 但是,如果不實現IDisposable本身,它就IDisposable 這就是為什么它需要Func<Stream>Func<TextReader>代替流或讀取器直接 - 它需要能夠推遲開放直到它需要它。 它是迭代器本身(由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
{
    /// <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();
        }
    }
}

問題在於:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM