Подтвердить что ты не робот

Проблема 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);
}

Я, вероятно, пропущу что-то действительно основное, но я не вижу его.

4b9b3361

Ответ 1

Чтобы прочитать все строки в 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());
                    }
                }
            }
        }
    }
}

Ответ 2

Если вы хотите прочитать файл по строкам через 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);
        }

И снова все оцениваются лениво (без буферизации).

Ответ 3

У меня есть класс LineReader в моем проекте MiscUtil. Он немного более общий, чем предлагаемые здесь решения, в основном с точки зрения способа его создания:

  • От функции, возвращающей поток, и в этом случае он будет использовать UTF-8
  • От функции, возвращающей поток, и кодирования
  • От функции, которая возвращает текстовый считыватель
  • От имени файла, в этом случае он будет использовать UTF-8
  • От имени файла и кодировки

Класс "владеет" любыми ресурсами, которые он использует, и соответствующим образом закрывает их. Однако он делает это без реализации IDisposable. Именно поэтому вместо потока или считывателя он берет Func<Stream> и Func<TextReader> - он должен уметь откладывать отверстие, пока оно ему не понадобится. Это сам итератор (который автоматически располагается в цикле foreach), который закрывает ресурс.

Как отметил Марк, это хорошо работает в 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();
        }
    }
}

Ответ 4

Проблема заключается в следующем:

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

Потому что "файл" - это строка, а строка реализует IEnumerable. Но этот перечислитель возвращает "char" и "char" не может быть принудительно преобразован в строку.

Вы должны использовать цикл while, как вы сказали.

Ответ 5

Чуть более элегантным является следующее...

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

Ответ 6

Мне нравится домашнее задание;)

Вы повторяете имя файла (строку), которое дает вам по одному символу за раз. Просто используйте подход while, который правильно использует sr.ReadLine().

Ответ 7

Вместо использования StreamReader, а затем, пытаясь найти строки внутри переменной String file, вы можете просто использовать File.ReadAllLines:

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

Ответ 8

Вы перечисляете строку, и когда вы делаете это, вы берете один char в то время.

Вы уверены, что это то, что вы хотите?

foreach(string line in file)

Ответ 9

Простейший (не эффективный с точки зрения памяти) подход к итерации каждой строки в файле

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

Ответ 10

Я предполагаю, что вы хотите что-то вроде этого:

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