7.8. Conceptos básicos sobre ficheros
Llega el momento de ver algunos conceptos que hemos pasado por encima, y que es necesario conocer:
- Fichero físico. Es un fichero que existe realmente en el disco, como "agenda.txt".
- Fichero lógico. Es un identificador que aparece dentro de nuestro programa, y que permite acceder a un fichero físico. Por ejemplo, si declaramos "StreamReader fichero1", esa variable "fichero1" representa un fichero lógico.
- Equivalencia entre fichero lógico y fichero físico. No necesariamente tiene por qué existir una correspondencia "1 a 1": puede que accedamos a un fichero físico mediante dos o más ficheros lógicos (por ejemplo, un StreamReader para leer de él y un StreamWriter para escribir en él), y también podemos usar un único fichero lógico para acceder a distintos ficheros físicos (por ejemplo, los mapas de los niveles de un juego, que estén guardados en distintos ficheros físicos, pero los abramos y los leamos utilizando siempre una misma variable).
- Registro: Un bloque de datos que forma un "todo", como el conjunto de los datos de una persona: nombre, dirección, e-mail, teléfono, etc. Cada uno de esos "apartados" de un registro se conoce como "campo".
- Acceso secuencial: Cuando debemos "recorrer" todo el contenido de un fichero si queremos llegar hasta cierto punto (como ocurre con las cintas de video o de casete). Es lo que estamos haciendo hasta ahora en los ficheros de texto.
- Acceso aleatorio: Cuando podemos saltar hasta cualquier posición del fichero directamente, sin necesidad de recorrer todo lo anterior. Es algo que comenzaremos a hacer pronto.
7.9. Leer datos básicos de un fichero binario
Los ficheros de texto son habituales, pero es aún más frecuente encontrarnos con ficheros en los que la información está estructurada como una secuencia de bytes, más o menos ordenada. Esto ocurre en las imágenes, los ficheros de sonido, de video, etc.
Vamos a ver cómo leer de un fichero "general", y lo aplicaremos a descifrar la información almacenada en ciertos formatos habituales, como una imagen BMP o un sonido MP3.
Como primer acercamiento, vamos a ver cómo abrir un fichero (no necesariamente de texto) y leer el primer byte que contiene. Usaremos una clase específicamente diseñada para leer datos de los tipos básicos existentes en C# (byte, int, float, etc.), la clase "BinaryReader":
/*---------------------------*/ /* Ejemplo en C# nº 77: */ /* ejemplo77.cs */ /* */ /* Ficheros binarios (1) */ /* */ /* Introduccion a C#, */ /* Nacho Cabanes */ /*---------------------------*/ using System; using System.IO; public class Ejemplo77 { public static void Main() { BinaryReader fichero; string nombre; byte unDato; Console.WriteLine("Introduzca el nombre del fichero"); nombre = Console.ReadLine(); try { fichero = new BinaryReader( File.Open(nombre, FileMode.Open)); unDato = fichero.ReadByte(); Console.WriteLine("El byte leido es un {0}", unDato); fichero.Close(); } catch (Exception exp) { Console.WriteLine(exp.Message); return; } } }
La forma de abrir un BinaryReader es algo más incómoda que con los ficheros de texto, porque nos obliga a llamar a un constructor y a indicarle ciertas opciones sobre el modo de fichero (la habitual será simplemente "abrirlo", con "FileMode.Open"), pero a cambio podemos leer cualquier tipo de dato, no sólo texto: ReadByte lee un dato "byte", ReadInt32 lee un "int", ReadSingle lee un "float", ReadString lee un "string", etc.
Ejercicios propuestos:
- Abrir un fichero con extensión EXE y comprobar si realmente se trata de un ejecutable, mirando si los dos primeros bytes del fichero corresponden a una letra "M" y una letra "Z", respectivamente.
7.10. Leer bloques de datos de un fichero binario
Leer byte a byte (o float a float) puede ser cómodo, pero también es lento. Por eso, en la práctica es más habitual leer grandes bloques de datos. Típicamente, esos datos se guardarán como un array de bytes.
Para eso, usaremos la clase "FileStream", más genérica. Esta clase tiene método "Read", que nos permite leer una cierta cantidad de datos desde el fichero. Le indicaremos en qué array guardar esos datos, a partir de qué posición del array debe introducir los datos, y qué cantidad de datos se deben leer. Nos devuelve un valor, que es la cantidad de datos que se han podido leer realmente (porque puede ser menos de lo que le hemos pedido, si hay un error o estamos al final del fichero).
Para abrir el fichero usaremos "OpenRead", como en este ejemplo:
/*---------------------------*/ /* Ejemplo en C# nº 78: */ /* ejemplo78.cs */ /* */ /* Ficheros binarios (2) */ /* */ /* Introduccion a C#, */ /* Nacho Cabanes */ /*---------------------------*/ using System; using System.IO; public class Ejemplo78 { 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]); } fichero.Close(); } catch (Exception exp) { Console.WriteLine(exp.Message); return; } } }
Ejercicios propuestos:
- Abrir un fichero con extensión EXE y comprobar si realmente se trata de un ejecutable, mirando si los dos primeros bytes del fichero corresponden a una letra "M" y una letra "Z", respectivamente. Se deben leer ambos bytes a la vez, usando un array.