c#

Problema FileStream StreamReader en C#



(10)

Estoy probando el funcionamiento de las clases FileStream y StreamReader. A través de una aplicación de consola. Intento entrar en un archivo y leer las líneas e imprimirlas en la consola.

He podido hacerlo con un ciclo while, pero quiero probarlo con un ciclo 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); } } } } } }

El error que sigo recibiendo es: No se puede convertir el tipo ''char'' en ''cadena''

El ciclo while, que funciona, se ve así:

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

Probablemente estoy pasando por alto algo realmente básico, pero no puedo verlo.


El problema está en:

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

Es porque el "archivo" es una cadena, y la cadena implementa IEnumerable. Pero este enumerador devuelve "char" y "char" no se puede convertir implícitamente a cadena.

Deberías usar el ciclo while, como dices.


En lugar de usar un StreamReader y luego tratar de encontrar líneas dentro de la variable del String file , simplemente puede usar File.ReadAllLines :

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


Está enumerando una cadena, y cuando lo hace, toma un char en ese momento.

¿Estás seguro de que esto es lo que quieres?

foreach(string line in file)


Me parece una tarea;)

Estás iterando sobre el nombre del archivo (una cadena), lo que te da un carácter a la vez. Simplemente use el enfoque while que usa correctamente sReReignLine ().


Para leer todas las líneas en 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()); } } } } } }


Si desea leer un archivo línea por línea a través de foreach (de manera reutilizable), considere el siguiente bloque de iteradores:

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

Tenga en cuenta que esto se evalúa de forma perezosa: no hay ninguno de los File.ReadAllLines() que asociaría con File.ReadAllLines() . La sintaxis foreach asegurará que el iterador sea Dispose() d correctamente incluso para las excepciones, cerrando el archivo:

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

(este bit se agrega solo por interés ...)

Otra ventaja de este tipo de abstracción es que funciona muy bien con LINQ, es decir, es fácil hacer transformaciones / filtros, etc. con este enfoque:

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

Y de nuevo, todos evaluados perezosamente (sin almacenamiento en búfer).


Supongo que quieres algo como esto:

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


Tengo una clase LineReader en mi proyecto MiscUtil . Es un poco más general que las soluciones dadas aquí, principalmente en términos de la forma en que puedes construirlo:

  • De una función que devuelve una secuencia, en cuyo caso usará UTF-8
  • Desde una función que devuelve una secuencia, y una codificación
  • De una función que devuelve un lector de texto
  • De solo un nombre de archivo, en cuyo caso usará UTF-8
  • De un nombre de archivo y una codificación

La clase "posee" los recursos que utiliza y los cierra de forma adecuada. Sin embargo, lo hace sin implementar IDisposable sí. Esta es la razón por la que se necesita Func<Stream> y Func<TextReader> lugar de la secuencia o el lector directamente; necesita poder diferir la apertura hasta que lo necesite. Es el propio iterador (que se elimina automáticamente mediante un ciclo foreach ) que cierra el recurso.

Como Marc señaló, esto funciona muy bien en LINQ. Un ejemplo que me gusta dar es:

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;

Esto transmitirá todos los errores de un montón de archivos de registro, abriéndolos y cerrándolos a medida que avanzan. Combinado con Push LINQ, puedes hacer todo tipo de cosas agradables :)

No es una clase particularmente "complicada", pero es realmente útil. Aquí está la fuente completa, por conveniencia, si no desea descargar MiscUtil. La licencia del código fuente está aquí .

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


Un enfoque simplista (no eficiente de la memoria) de iterar cada línea en un archivo es

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


Un poco más elegante es el siguiente ...

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