Archivos en C#

La manera de almacenar y recuperar información que perdure en el tiempo se basa en el uso de “memoria secundaria”, compuesta esencialmente por discos (diskettes, discos duros, CD, DVD, etc.) y ocasionalmente cintas. En cualquiera de estos medios, la unidad de almacenamiento de información se denomina archivo.

Streams

La lectura y escritura a un archivo son hechas usando un concepto genérico llamado stream. La idea detrás del stream existe hace tiempo, cuando los datos son pensados como una transferencia de un punto a otro, es decir, como un flujo de datos. En el ambiente .NET se puede encontrar muchas clases que representan este concepto que trabaja con archivos o con datos de memoria (como se muestra en la figura de abajo).

                                   Clases del Framework .NET para el uso de Streams.

Un stream es como se denomina a un objeto utilizado para transferir datos. Estos datos pueden ser transferidos en dos posibles direcciones:
  • Si los datos son transferidos desde una fuente externa al programa, entonces se habla de “leer desde el stream”.
  • Si los datos son transferidos desde el programa a alguna fuente externa, entonces se habla de “escribir al stream”.
Frecuentemente, la fuente externa será un archivo, pero eso no necesariamente es el caso, por lo que el concepto es utilizado ampliamente con fuentes de información externas de diversos tipos. Algunas otras posibilidades fuera de los archivos incluyen:
  • Leer o escribir datos a una red utilizando algún protocolo de red, donde la intención es que estos datos sean recibidos o enviados por otro computador.
  • Lectura o escritura a un área de memoria.
  • La Consola
  • La Impresora
  • Otros 
Algunas clases que C# provee para resolver este acceso a fuentes diversas incluyen las clases de tipo: Reader y Writer.

BufferedStream

Esta clase se utiliza para leer y para escribir a otro stream. Se utiliza por razones del performance, cuando el caché de los datos del archivo es utilizado por el sistema operativo subyacente. 

El uso de streams para la lectura y escritura de archivo es directa pero lenta con bajo performance. Por esta razón la clase BufferedStream existe y es más eficiente. Puede ser utilizado por cualquier clase de stream. Para operaciones de archivo es posible utilizar FileStream, donde el buffering está ya incluido. 

Leyendo desde un archivo usando BufferedStream

using System;
using System.Text;
using System.IO;

static void Main(string[] args)
{
    string path = "c:\\sample\\sample.xml";
    Stream instream = File.OpenRead(path);

    // crear buffer para abrir stream
    BufferedStream bufin = new BufferedStream(instream);
    byte[] bytes = new byte[128];

    // leer los primeros 128 bytes del archivo
    bufin.Read(bytes, 0, 128);
    Console.WriteLine("Allocated bytes: "+Encoding.ASCII.GetString(bytes));
}

Leyendo desde un archivo de texto

using System;
using System.IO;

static void Main(string[] args)
{
    string fileName = "temp.txt";
    FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
    StreamReader reader = new StreamReader(stream);

    while (reader.Peek() > -1) Console.WriteLine(reader.ReadLine());
    reader.Close();
}

Escribiendo en un archive de texto

using System;
using System.IO;

static void Main(string[] args)
{
    string fileName = "temp.txt";
    FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
    StreamWriter writer = new StreamWriter(stream);

    writer.WriteLine("Esta es la primera línea del archivo.");
    writer.Close();
}

Creando un archivo y escribiendo en este

Este ejemplo usa el método CreateText() el cual crea un Nuevo archive y retorna un objeto treamWriter que escribe a un archivo usando formato UTF-8.

using System;
using System.IO;

static void Main(string[] args)
{
    string fileName = "temp.txt";
    StreamWriter writer = File.CreateText(fileName);

    writer.WriteLine("Este es mi Nuevo archivo creado.");
    writer.Close();
}

Insertando texto en un archivo

using System;
using System.IO;

static void Main(string[] args)
{
    try
    {
        string fileName = "temp.txt";
        // esto inserta texto en un archivo existente, si el archivo no existe lo crea
        StreamWriter writer = File.AppendText(fileName);
        writer.WriteLine("Este es el texto adicionado.");
        writer.Close();
    }
    catch
    {
        Console.WriteLine("Error");
    }
}

Leyendo un archivo binario

using System;
using System.IO;

static void Main(string[] args)
{
    try
    {
        string fileName = "temp.txt";
        int letter = 0;
        FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
        BinaryReader reader = new BinaryReader(stream);

        while (letter != -1)
        {
            letter = reader.Read();
            if (letter != -1) Console.Write((char)letter);
        }
        reader.Close();
        stream.Close();
    }
    catch
    {
        Console.WriteLine("Error");
    }
}

Escribiendo en un archivo binario

static void Main(string[] args)
{
    try
    {
        string fileName = "temp.txt";
        // data a ser guardada
        int[] data = {0, 1, 2, 3, 4, 5};
        FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Write);
        BinaryWriter writer = new BinaryWriter(stream);

        for(int i=0; i<data.Length; i++)
        {
            // números son guardados en formáto UTF-8 format (4 bytes)
            writer.Write(data[i]);
        }

        writer.Close();
        stream.Close();
    }
    catch
    {
        Console.WriteLine("Error");
}

Ver cambios en el file system

.NET Framework provee la clase System.IO.FileSystemWatcher la cual permite ver si hay cambios en el file system.

using System;
using System.IO;
class WatcherSample
{
    static void Main(string[] args)
    {
        // ver los cambios en el directorio de la aplicación y sobre todos los archivos
        FileSystemWatcher watcher = new FileSystemWatcher(System.Windows.Forms.Application.StartupPath, "*.*");

        // ver el nombre del archivo y tamaño cambiado
        watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
        watcher.Changed += new FileSystemEventHandler(OnChange);
        watcher.Created += new FileSystemEventHandler(OnChange);
        watcher.Deleted += new FileSystemEventHandler(OnChange);
        watcher.Renamed += new RenamedEventHandler(OnChange);
        watcher.EnableRaisingEvents = true;

        // espera por una tecla para terminar la aplicación
        Console.ReadLine();
    }
    private static void OnChange(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine("File: {0} - change type: {1}", e.FullPath, e.ChangeType);
    }
    private static void OnChange(object sender, RenamedEventArgs e)
    {
        Console.WriteLine("File: {0} renamed to {1}", e.OldName, e.Name);