Posteado por: fzapataramirez | Viernes, Noviembre 14, 2008

Lectura y escritura de archivos en c#

El manejo de archivos en la plataforma .NET se logra mediante la clase Stream que representa un flujo de información (Un archivo es considerado un flujo de datos, al igual que los datos transferidos de un dispositivo a otro, o los datos transferidos por la red mediante TCP/IP).

La clase Stream, es una clase abstracta, por lo que no es posible utilizarla directamente ya que no se puede crear instancias. Lo que se debe hacer es utilizar una de sus clases derivadas que se especializan en el tratamiento de streams para diferentes destinos como por ejemplo FileStream (para el manejo de archivos), Memorystream (para el manejo de datos en memoria), etc.

La primera opción para escribir y/o leer datos de una archivo de texto es utilizar la clase FileStream. Esta clase se encuentra en el namespace System.IO y es una clase derivada de la clase Stream.

El FileStream actúa como un intermediario entre el sistema de archivos y nuestra aplicación, permitiendo realizar de una manera limpia y sencilla operaciones de escritura y lectura en archivos. Para utilizar el FileStream, lo primero que se debe hacer es crear una instancia que apunte al archivo deseado. Para esto se tiene la opción de utilizar alguno de los constructores ofrecidos, tal y como se muestra a continuación:

clip_image002

Como se ve en la figura anterior, es un constructor bastante tedioso de utilizar porque recibe una cantidad considerable de parámetros. Para contrarestar esto, tenemos la posibilidad de utilizar alguno de los métodos ofrecidos por la clase utilitaria File, que nos permite obtener un FileStream con parámetros específicos de acuerdo al método utilizado para obtenerlo. A continuación se muestra un ejemplo:

clip_image004

En el ejemplo anterior se utilizó el método Create el cual crea el archivo en la ruta especificada y en caso de que el archivo exista, es sobrescrito. Estos métodos ofrecidos por la clase File, facilitan el desarrollo de aplicaciones con manejo de archivos y permite una mejor lectura de código.

Para escribir datos en un archivo de texto, se utilizan los métodos Write y WriteByte. El primero recibe como parámetro un vector de bytes que representa la información a almacenar, mientras el segundo recibe como parámetro un solo byte para ser escrito. Elegir cual de los dos métodos utilizar depende del escenario, ya que cuando se va a escribir gran cantidad de información, no es recomendable escribir byte a byte porque se ejecutaría muchas veces el mismo ciclo sino que por el contrario, es conveniente escribir varios bytes a la vez.

A continuación se muestra un ejemplo de la operación de escritura con ambos métodos:

clip_image006

Se puede observar como se utiliza la clase Encoding para formatear la cadena en UTF8 y obtener el array de bytes que se debe escribir en el archivo. De la misma manera se puede codificar el texto a escribir en otro formato como por ejemplo ASCII, Unicode, etc.

Es importante mencionar que todo stream se debe cerrar al finalizar la operación que se realiza sobre el, ya que de lo contrario el archivo quedaría bloqueado y no se liberarían recursos del sistema operativo. Adicionalmente, el método Flush, se utiliza para indicarle a la clase FileStream, que escriba en el archivo físico lo que se ha escrito con el método WriteByte o con el método Write hasta el momento ya que de lo contrario, dichos cambios se verían reflejados únicamente cuando se cierre el Stream. Este método seria útil cuando queremos que se escriba el texto en el archivo y no deseamos cerrar el Stream porque necesitamos seguir haciendo operaciones de escritura, además de ser una buena práctica siempre utilizarlo aun cuando parezca ser innecesario.

La implementación de la misma funcionalidad con el otro método, seria como se muestra a continuación.

clip_image008

Como se ve en el ejemplo anterior, con estos métodos, el programador tiene un control muy preciso sobre la información que desea escribir en el archivo, tanto, que hasta puede llegar a ser engorroso en algunas ocasiones.

Afortunadamente, la plataforma .NET ofrece otras maneras de administrar Streams para hacerle la vida más fácil al desarrollador. Una de esas maneras es utilizar la clase StreamWriter para escribir en archivos y StreamReader para leer desde archivos. Es necesario tener en cuenta que estas clases están orientadas a obtener caracteres como salida a diferencia de las clases que heredan de Stream que están orientadas a obtener bytes.

Con la clase StreamWriter, solo nos debemos preocupar por utilizar el método Write o WriteLine para escribir datos en un archivo de texto. Estos métodos reciben tipos de datos nativos del lenguaje como por ejemplo int, bool, decimal, float, string, char, etc. Y utiliza el codificador indicado cuando se instancia la clase para codificar los caracteres de salida al archivo de texto. En caso de utilizar un constructor que no especifique algún Encoding, por defecto esta clase opera con el UTF8.

clip_image010

Como se ve en el ejemplo anterior, las líneas de código son mucho mas sencillas que las vistas en el ejemplo del FileStream. Esto es porque la clase StreamWriter es una clase especializadas para el trabajo de Stream hacia archivos de texto y nos evita el trabajo de tener que obtener la representación en bytes del valor que queramos escribir.

La diferencia entre el método Write y WriteLine, es que el segundo inserta un salto de línea al final de los datos ingresados, haciendo que la próxima vez que se quiera insertar, se hará en la siguiente línea.

Aunque la clase StreamWriter no hereda de la clase Stream, si utiliza en su implementación un Stream en el cual escribe o lee secuencias de caracteres. Por eso, esta clase en su interior, se encarga de crear y utilizar el Stream necesario para apuntar al archivo de texto representado por la ruta que se utilizo en el constructor del ejemplo. Sin embargo, esta clase también tiene otro constructor donde se le puede pasar un Stream que tengamos creado en lugar de la ruta, como se ve a continuación:

clip_image012

El complemento del objeto StreamWriter es el objeto StreamReader, cuyo principal objetivo es facilitarnos las tareas de lectura de cadenas de caracteres. Con este objeto, nos podemos despreocupar de esas tareas de bajo nivel para poder obtener un código mucho mas limpio y fácilmente legible.

clip_image014

En el ejemplo se ve como fácilmente se puede leer información de un archivo de texto utilizando el método ReadLine del objeto StreamReader. Este método, simplemente lee la línea siguiente teniendo en cuenta la posición actual del puntero del archivo.

Es importante aclarar que utilizando los streams vistos hasta el momento se pueden leer y escribir en archivos de texto cadenas de caracteres, es decir, texto plano. Esto implica que la información escrita en los archivos podrá ser vista y entendida por cualquier persona, ya que se guarda tal y como se encuentra en un principio.

Lo anterior puede llegar a ser un problema en los casos en que se elige utilizar archivos de texto como repositorio de datos de una aplicación (aunque esto es poco recomendable, habrán casos en los pueda llegar a ser necesario), y digo que es un problema porque podría haber información que no queremos que cualquier persona la pueda ver y mucho menos entender, sino que quisiéramos que dicha información estuviera almacenada de forma segura.

Una posible opción para esto es almacenando la información en formato binario, es decir, que la información estará almacenada no en formato de texto plano sino en su representación binaria (1s y 0s), lo cual permite tener mas privacidad (OJO, solo mas privacidad, no mucha privacidad ni seguridad. Si el punto fuera como obtener una forma segura de almacenar información tendríamos que mirar los temas de cifrado y hashing). En .NET, el objeto que nos facilita trabajar con Stream de datos binarios es el BinaryWriter y el BinaryReader, los cuales supongo pueden deducir como se utilizan según lo visto anteriormente.

El primero permite escribir datos en un archivo de texto en formato binario mediante el método Write(), el cual.tiene varias sobrecargas entre los cuales acepta varios tipos de dato como por ejemplo int, long, bool, double, decimal, char, string. El segundo permite recuperar información almacenada en formato binario por medio del método indicado. Como así que por medio del método indicado?. Lo que sucede con el BinaryReader es que no tiene un método Read genérico, sino que tiene un Read para cada tipo de dato que puede ser escrito, por ejemplo ReadInt32, ReadBoolean, ReadChar, etc. Esto nos obliga a conocer la estructura del archivo con el que estamos trabajando para poder leer el tipo de dato correcto en la posición correcta.

A continuación se muestra un pequeño ejemplo sobre como utilizar el BinaryReader y el BinaryWriter.

clip_image016

En el ejemplo anterior se observa como se puede escribir varios tipos de dato con el mismo método Write del objeto BinaryWriter ya que tiene varias sobrecargas.

A continuación se muestra como se vería la información en el caso de intentar visualizarla con un editor de texto:

clip_image018

Para leer información binaria se utiliza el objeto BinaryReader y se utiliza “El método indicado”.

clip_image020

En el ejemplo anterior se observa como se utiliza el método de lectura según el tipo de dato que desee leer, lo cual implica que con anterioridad se conozca la estructura del archivo. En este caso, nótese que se leyó exactamente en el mismo orden en el que se escribió la información con el BinaryWriter.

El siguiente es el resultado de la ejecución del código anterior:

clip_image022


Respuestas

  1. Se puede que tenga una cadena por ejemplo
    “Esta es mi cadena”

    y al escribirlo al archivo lo escriba de esta forma???

    “Esta es mi cadena”

  2. Claro que sí, utilizando la clase StreamWriter se puede almacenar la infomracion en cadenas de caracteres sobre archivos de texto.

    Simplemente debes usar el método Write sobre una instancia de la clase StreamWriter

    Saludos

  3. Gracias por la respuesta pero creo q no salió mi comentario completo, a lo q me refiero es q si tengo mi cadena “Esta es mi nueva cadena” puedo truncar esa cadena en “Esta es” y una línea abajo escriba el resto de la cadena “mi nueva cadena”?

  4. Claro que si

    Tienes dos opciones.

    1. modificar la cadena original, insertando la secuencia de caracteres “\n” lo cual indica que se inserte un salto de linea. Por lo tanto tu nueva cadena seria “Esta es \n mi nueva cadena” y luego utilizas el metodo Write de la clase StreamWriter.

    2. Utilizar dos cadenas en lugar de una y utilizar el metodo WriteLine, asi:

    string cadena1= “Esta es”;
    string cadena2= “mi nueva cadena”;
    writer.WriteLine(cadena1);
    writer.Write(cadena2);

    donde writer es una instancia de la calse StreamWriter.

    Saludos y espero que te sirva.

  5. como guardo un archivo de texto en c#
    escribir y guardar

  6. Hola Luis.

    Creo que en la entrada se explica bastante claro…………..

    La plataforma .NET ofrece otras maneras de administrar Streams para hacerle la vida más fácil al desarrollador. Una de esas maneras es utilizar la clase StreamWriter para escribir en archivos y StreamReader para leer desde archivos. Es necesario tener en cuenta que estas clases están orientadas a obtener caracteres como salida a diferencia de las clases que heredan de Stream que están orientadas a obtener bytes.

    Con la clase StreamWriter, solo nos debemos preocupar por utilizar el método Write o WriteLine para escribir datos en un archivo de texto. Estos métodos reciben tipos de datos nativos del lenguaje como por ejemplo int, bool, decimal, float, string, char, etc. Y utiliza el codificador indicado cuando se instancia la clase para codificar los caracteres de salida al archivo de texto. En caso de utilizar un constructor que no especifique algún Encoding, por defecto esta clase opera con el UTF8.

    Si te queda alguna duda, puedes leer la entrada completa.

    Suerte !!!

  7. Como genero un archivo en formato ASCII

    • Hola Andrea

      Es tan simple como usar el constructor de la clase StreamWriter que recibe como parametro el tipo de Encoding en el que queremos formatear el archivo. Asi:

      FileStream stream = new FileStream(”nuevo.txt”, FileMode.Create);
      StreamWriter sw = new StreamWriter(new FileStream(), Encoding.ASCII);

      Saludos

  8. una duda como manejarias una agenda de la manera de q esvcriba en un txt los datos siguienets nombre apellido tel edad direccion y por medio de la consola meta los datos¿

    • Pues sin duda alguna si tu opción es manejar esos datos en archivos de texto, te recomiendo hacerlo mediante XmlReader y XmlWriter, las cuales son clases que permiten escribir información en archivos de texto en formato xml y asi poder utilizar archivos de texto como un repositorio de datos.

      Algo que tienes que analizar bien, es la confidencialidad de los datos. Que pasa si alguien tiene acceso a la información, es algo critico?. Si lo es, deberás almacenar los datos encriptados o buscar otro repositorio de datos donde el acceso sea controlado como una base de datos.

      Saludos

  9. hola como estan… solo tengo una dudita… ahora estoy haciendo un proyecto en C#2005 en el cual creo archivos de texto en tiempo de ejecucion… los cuales me interesa leer en otro proceso luego de crearlos… la cuestion es que C# cuando quiero leer el archivo me dice que el archivo que deseo abrir no existe… ya intente solucionarlo pero nada… si me podrian ayudar Gracias de antemano

    • Hola Eduardo,

      Primero que todo asegurate que el proceso que crea el archivo de texto efectivamente lo este creando y que ademas estes cerrando el Stream mediante el cual escribes los datos ya que dicha información no se ve reflejada fisicamente en el archivo hasta que utilices el método Flush o cierrres el Stream.
      Si continuas teniendo problemas con esto, te pido que seas mas especifico y muestres como estas haciendo los procesos para detectar el problema.

      Saludos

  10. Gracias por sus respuestas…. enserio que amables…
    Bueno pues la verdad ya revise si cierro los streams y si lo hago… en realidad es un poco dificil de explicar…

    Lo que estoy haciendo es generar codigo de C++ desde una aplicacion de C#.

    Entonces creo un programa en C++ el cual lo compilo y genera el archivo de texto… ocea el archivo de texto lo genero con codigo de C++… de la siguiente forma…

    FILE* stream;
    stream = fopen(”C:\\output.txt”,”a+”);
    fprintf(stream,mensaje);
    fclose(stream);

    y si genera el archivo… luego de generar este archivo tengo que leerlo desde C#… para mostrar los resultados de los procesos que hago en C++…

    Esta fue la unica forma que encontre de mostrar los resultados del los procesos de C++ en C#…

    entonces desde C# tengo algo asi a grandes razgos…

    CrearYCompilarProgramaCmasmas();
    LeerArchivoGenerado();

    en el metodo de CrearYCompilarProgramaCmasmas(); hago el archivo de texto…
    y luego lo quiero leer con el metodo de LeerArchivoGenerado();

    lo leo de esta forma.

    StreamReader objReader = new StreamReader(”C:\\output.txt”);
    string linea = “”;

    while (linea != null)
    {
    linea = objReader.ReadLine();
    if (linea != null)
    txtOutput.AppendText(linea + “\n”);
    }
    objReader.Close();

    NO se si me di a entender jaja pero eso es lo que hago y cuando quiero leer el archivo me da la excepcion en esta parte:

    StreamReader objReader = new StreamReader(”C:\\output.txt”);

    me dice que no existe el archivo… pero si existe… porque si lo verifique
    Gracias por la ayuda que me puedan brindar

    • Hola Eduardo

      Pues la verdad no se que decirte, aparentemente todo esta bien (por lo menos la parte de lectura desde c#).
      Lo único que se me ocurre es que estes ejecutando eso en un Windows Vista y que te este bloqueando la aplicacion de c# para acceder el C.

      Tambien verifica que puedas acceder mediante un editor de texto al archivo output.txt para descartar posibles errores en el archivo.

  11. ok gracias por contestarme… lo revisare hasta encontrar el problem…

    Gracias de nuevo :)

  12. Hola y hay algun metodo de busqueda para encontrar palabras en un archivo binario?

  13. Hola, tengo una cadena de texto separada por comas, ya la puedo ver en la consola y en una aplicación de escritorio. Ahora como debería hacer para sacarle las comas(separadores) al txt? muchas gracias por la ayuda.

    • Puedes utilizar el mtodo Split del objeto String. Ese mtodo recibe como parametro un array de stings que representan los separadores (en tu caso, un array de una posicion que contiene una coma) y te retorna un array de strings como resultado de separar el texto.

      saludos


Dejar una respuesta

Su respuesta:

Categorías