Introducción a C#
Por Nacho Cabanes, versión 0.93 de 16-abr-2010


(Nota: Estás viendo una versión del curso antigua, creada en 2009. Es recomendable que sigas la versión 2015, mucho más actualizada, con contenidos más detallados, más ejemplos y más ejercicios propuestos)

7.11. La posición en el fichero

La clase FileStream tiene también un método ReadByte, que permite leer un único byte. En ese caso (y también a veces en el caso de leer todo un bloque), habitualmente nos interesará situarnos antes en la posición exacta en la que se encuentra el dato que nos interesa leer, y esto podemos conseguirlo mediante acceso aleatorio, sin necesidad de leer todos los bytes anteriores.

Para ello, tenemos el método "Seek". A este método se le indican dos parámetros: la posición a la que queremos saltar, y el punto desde el que queremos que se cuente esa posición (desde el comienzo del fichero –SeekOrigin.Begin-, desde la posición actual –SeekOrigin.Current- o desde el final del fichero –SeekOrigin.End-). La posición es un Int64, porque puede ser un número muy grande e incluso un número negativo (si miramos desde el final del fichero, porque desde él habrá que retroceder).

De igual modo, podemos saber en qué posición del fichero nos encontramos, consultando la propiedad "Position", así como la longitud del fichero, mirando su propiedad "Length", como en este ejemplo:

/*---------------------------*/
/*  Ejemplo en C# nº 79:     */
/*  ejemplo79.cs             */
/*                           */
/*  Ficheros binarios (3)    */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
using System.IO;
 
public class Ejemplo79
{
 
  public static void Main()
  {
    FileStream fichero;
    string nombre;
    byte[] datos;
    int cantidadLeida;
 
    Console.WriteLine("Introduzca el nombre del fichero");
    nombre = Console.ReadLine();
 
    try 
    {
      fichero = File.OpenRead(nombre);
      datos = new byte[10];
      int posicion = 0;
      int cantidadALeer = 10;
      cantidadLeida = fichero.Read(datos, posicion, cantidadALeer);
 
      if (cantidadLeida < 10)
        Console.WriteLine("No se han podido leer todos los datos!");
      else {
        Console.WriteLine("El primer byte leido es {0}", datos[0]);
        Console.WriteLine("El tercero es {0}", datos[2]);
      }
 
      if (fichero.Length > 30) {
        fichero.Seek(19, SeekOrigin.Begin);        
        int nuevoDato = fichero.ReadByte();
        Console.WriteLine("El byte 20 es un {0}", nuevoDato);
 
        Console.WriteLine("La posición actual es {0}",
          fichero.Position);
        Console.WriteLine("Y el tamaño del fichero es {0}",
          fichero.Length);    
      }
 
      fichero.Close();    
    } 
    catch (Exception exp)
    {
      Console.WriteLine(exp.Message);
      return;
    }
  }
 
}
 

(Nota: existe una propiedad "CanSeek" que nos permite saber si el fichero que hemos abierto permite realmente que nos movamos a unas posiciones u otras).

7.12. Escribir en un fichero binario

Para escribir en un FileStream, usaremos la misma estructura que para leer de él:

Además, a la hora de abrir el fichero, tenemos dos alternativas:

Vamos a ver un ejemplo que junte todo ello: crearemos un fichero, guardaremos datos, lo leeremos para comprobar que todo es correcto, añadiremos al final, y volveremos a leer:

/*---------------------------*/
/*  Ejemplo en C# nº 80:     */
/*  ejemplo80.cs             */
/*                           */
/*  Ficheros binarios (4):   */
/*  Escritura                */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
using System.IO;
 
public class Ejemplo80
{
 
  const int TAMANYO_BUFFER = 10;
 
  public static void Main()
  {
    FileStream fichero;
    string nombre;
    byte[] datos;
 
    nombre = "datos.dat";
    datos = new byte[TAMANYO_BUFFER];
 
    // Damos valores iniciales al array
    for (byte i=0; i<TAMANYO_BUFFER; i++)
      datos[i] = (byte) (i + 10);
 
    try 
    {      
      int posicion = 0;
 
      // Primero creamos el fichero, con algun dato
      fichero = File.Create( nombre );
      fichero.Write(datos, posicion, TAMANYO_BUFFER);
      fichero.Close();
 
      // Ahora leemos dos datos
      fichero = File.OpenRead(nombre);
      Console.WriteLine("El tamaño es {0}", fichero.Length);
      fichero.Seek(2, SeekOrigin.Begin);
      int nuevoDato = fichero.ReadByte();
      Console.WriteLine("El tercer byte es un {0}", nuevoDato);      
      fichero.Seek(-2, SeekOrigin.End);
      nuevoDato = fichero.ReadByte();
      Console.WriteLine("El penultimo byte es un {0}", nuevoDato);
      fichero.Close();
 
      // Ahora añadimos 10 datos más, al final
      fichero = File.OpenWrite(nombre);
      fichero.Seek(0, SeekOrigin.End);
      fichero.Write(datos, 0, TAMANYO_BUFFER);
      // y modificamos el tercer byte
      fichero.Seek(2, SeekOrigin.Begin);
      fichero.WriteByte( 99 );
      fichero.Close();
 
      // Volvemos a leer algun dato
      fichero = File.OpenRead(nombre);
      Console.WriteLine("El tamaño es {0}", fichero.Length);
      fichero.Seek(2, SeekOrigin.Begin);
      nuevoDato = fichero.ReadByte();
      Console.WriteLine("El tercer byte es un {0}", nuevoDato);
      fichero.Close();
 
    } 
    catch (Exception exp)
    {
      Console.WriteLine(exp.Message);
      return;
    }
  }
 
}
 

(Nota: existe una propiedad "CanWrite" que nos permite saber si se puede escribir en el fichero).

Si queremos que escribir datos básicos de C# (float, int, etc.) en vez de un array de bytes, podemos usar un "BinaryWriter", que se maneja de forma similar a un "BinaryReader", con la diferencia de que no tenemos métodos WriteByte, WriteString y similares, sino un único método "Write", que se encarga de escribir el dato que le indiquemos, sea del tipo que sea:

/*---------------------------*/
/*  Ejemplo en C# nº 81:     */
/*  ejemplo81.cs             */
/*                           */
/*  Ficheros binarios (5)    */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
using System.IO;
 
public class Ejemplo81
{
 
  public static void Main()
  {
    BinaryWriter ficheroSalida;
    BinaryReader ficheroEntrada;
    string nombre;
    // Los datos que vamos a guardar/leer
    byte unDatoByte;
    int unDatoInt;
    float unDatoFloat;
    double unDatoDouble;
    string unDatoString;
 
    Console.Write("Introduzca el nombre del fichero a crear: ");
    nombre = Console.ReadLine();
 
    Console.WriteLine("Creando fichero...");
    // Primero vamos a grabar datos
    try 
    {      
      ficheroSalida = new BinaryWriter(
        File.Open(nombre, FileMode.Create));
      unDatoByte = 1;
      unDatoInt = 2;
      unDatoFloat = 3.0f;
      unDatoDouble = 4.0;
      unDatoString = "Hola";      
      ficheroSalida.Write(unDatoByte);
      ficheroSalida.Write(unDatoInt);
      ficheroSalida.Write(unDatoFloat);
      ficheroSalida.Write(unDatoDouble);
      ficheroSalida.Write(unDatoString);
      ficheroSalida.Close();    
    } 
    catch (Exception exp)
    {
      Console.WriteLine(exp.Message);
      return;
    }
 
    // Ahora vamos a volver a leerlos
    Console.WriteLine("Leyendo fichero...");
    try 
    {
      ficheroEntrada = new BinaryReader(
        File.Open(nombre, FileMode.Open));
      unDatoByte = ficheroEntrada.ReadByte();
      Console.WriteLine("El byte leido es un {0}",
        unDatoByte);
      unDatoInt = ficheroEntrada.ReadInt32();
      Console.WriteLine("El int leido es un {0}",
        unDatoInt);
      unDatoFloat = ficheroEntrada.ReadSingle();
      Console.WriteLine("El float leido es un {0}",
        unDatoFloat);
      unDatoDouble = ficheroEntrada.ReadDouble();
      Console.WriteLine("El double leido es un {0}",
        unDatoDouble);
      unDatoString = ficheroEntrada.ReadString();
      Console.WriteLine("El string leido es \"{0}\"",
        unDatoString);
      Console.WriteLine("Volvamos a leer el int...");
      ficheroEntrada.BaseStream.Seek(1, SeekOrigin.Begin);
      unDatoInt = ficheroEntrada.ReadInt32();
      Console.WriteLine("El int leido es un {0}",
        unDatoInt);
      ficheroEntrada.Close();    
    } 
    catch (Exception exp)
    {
      Console.WriteLine(exp.Message);
      return;
    }
  }
 
}
 

Como se puede ver en este ejemplo, también podemos usar "Seek" para movernos a un punto u otro de un fichero si usamos un "BinaryReader", pero está un poco más escondido: no se lo pedimos directamente a nuestro fichero, sino al "Stream" (flujo de datos) que hay por debajo: ficheroEntrada.BaseStream.Seek(1, SeekOrigin.Begin);

El resultado de este programa es:


Introduzca el nombre del fichero a crear: 1234
Creando fichero...
Leyendo fichero...
El byte leido es un 1
El int leido es un 2
El float leido es un 3
El double leido es un 4
El string leido es "Hola"
Volvamos a leer el int...
El int leido es un 2

En este caso hemos usado "FileMode.Create" para indicar que queremos crear el fichero, en vez de abrir un fichero ya existente. Los modos de fichero que podemos emplear en un BinaryReader o en un BinaryWriter son los siguientes: