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.13. Ejemplo: leer información de un fichero BMP

Ahora vamos a ver un ejemplo un poco más sofisticado y un poco más real: vamos a abrir un fichero que sea una imagen en formato BMP y a mostrar en pantalla si está comprimido o no.

Para eso necesitamos antes saber cómo se guarda la información en un fichero BMP, pero esto es algo fácil de localizar en Internet:

FICHEROS .BMP 

Un fichero BMP está compuesto por las siguientes partes: 
una cabecera de fichero, una cabecera del bitmap, una tabla de colores 
y los bytes que definirán la imagen.

En concreto, los datos que forman la cabecera de fichero y la cabecera de bitmap 
son los siguientes:

TIPO DE INFORMACIÓN	- POSICIÓN EN EL ARCHIVO
Tipo de fichero (letras BM)	0-1
Tamaño del archivo	2-5
Reservado	6-7
Reservado	8-9
Inicio de los datos de la imagen	10-13
Tamaño de la cabecera de bitmap	14-17
Anchura (píxeles)	18-21
Altura (píxeles)	22-25
Número de planos	26-27
Tamaño de cada punto	28-29
Compresión (0=no comprimido)	30-33
Tamaño de la imagen	34-37
Resolución horizontal	38-41
Resolución vertical	42-45
Tamaño de la tabla de color	46-49
Contador de colores importantes	50-53

Con esta información nos basta para nuestro propósito: la compresión se indica en la posición 30 del fichero, es un entero de 4 bytes (lo mismo que un "int" en los sistemas operativos de 32 bits), y si es un 0 querrá decir que la imagen no está comprimida.

Como el bit menos significativo se almacena en primer lugar, nos podría bastar con leer sólo el byte de la posición 30, para ver si vale 0, y despreciar los 3 bytes siguientes. Entonces, lo podríamos comprobar así:

/*---------------------------*/
/*  Ejemplo en C# nº 82:     */
/*  ejemplo82.cs             */
/*                           */
/*  Ficheros binarios (6):   */
/*  Ver si un BMP está       */
/*  comprimido               */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
using System.IO;
 
public class Ejemplo82
{
 
  public static void Main()
  {
    FileStream fichero;
    string nombre;
    int compresion;
 
    Console.WriteLine("Comprobador de imágenes BMP\n");
    Console.WriteLine("Dime el nombre del fichero: ");
    nombre = Console.ReadLine();
 
    if (! File.Exists( nombre) )
    {
      Console.WriteLine("No encontrado!");
    }
    else
    {
      fichero = File.OpenRead(nombre);
      fichero.Seek(30, SeekOrigin.Begin);    
      compresion = fichero.ReadByte();
      fichero.Close();
 
      if (compresion == 0) 
        Console.WriteLine("Sin compresión");
      else
        Console.WriteLine("BMP Comprimido ");
    }
  }  
}
 

Ya que estamos, podemos mejorarlo un poco para que además nos muestre el ancho y el alto de la imagen, y que compruebe antes si realmente se trata de un fichero BMP:

/*---------------------------*/
/*  Ejemplo en C# nº 83:     */
/*  ejemplo83.cs             */
/*                           */
/*  Ficheros binarios (7):   */
/*  Información de un        */
/*  fichero BMP              */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
using System.IO;
 
public class Ejemplo83
{
 
  public static void Main()
  {
    FileStream fichero;
    string nombre;
    int compresion, ancho, alto;
    char marca1, marca2;
    byte[] datosTemp = new byte[4];
 
 
    Console.WriteLine("Comprobador de imágenes BMP\n");
    Console.WriteLine("Dime el nombre del fichero: ");
    nombre = Console.ReadLine();
 
    if (! File.Exists( nombre) )
    {
      Console.WriteLine("No encontrado!");
    }
    else
    {
      fichero = File.OpenRead(nombre);
      // Leo los dos primeros bytes
      marca1 = Convert.ToChar( fichero.ReadByte() );
      marca2 = Convert.ToChar( fichero.ReadByte() );
 
      if ((marca1 =='B') && (marca2 =='M')) { // Si son BM
        Console.WriteLine("Marca del fichero: {0}{1}", 
          marca1, marca2);
 
        fichero.Seek(18, SeekOrigin.Begin);  // Posición 18: ancho        
        fichero.Read(datosTemp, 0, 4);
        ancho = datosTemp[0] +               // Convierto 4 bytes a Int32
           datosTemp[1] * 256  + datosTemp[2] * 256  * 256
          + datosTemp[3] * 256 * 256 * 256;
        Console.WriteLine("Ancho: {0}", ancho);
 
        fichero.Read(datosTemp, 0, 4);
        alto = datosTemp[0] +               // Convierto 4 bytes a Int32
           datosTemp[1] * 256  + datosTemp[2] * 256  * 256
          + datosTemp[3] * 256 * 256 * 256;
        Console.WriteLine("Alto: {0}", alto);
 
        fichero.Seek(4, SeekOrigin.Current); // 4 bytes después: compresión
        compresion = fichero.ReadByte();
        fichero.Close();
 
        switch (compresion) {
          case 0: Console.WriteLine("Sin compresión"); break;
          case 1: Console.WriteLine("Compresión RLE 8 bits"); break;
          case 2: Console.WriteLine("Compresión RLE 4 bits"); break;
        }
      } else
        Console.WriteLine("No parece un fichero BMP\n");  // Si la marca no es BM      
    }
  }  
}
 

También podemos hacer lo mismo empleando un "BinaryReader", en lugar de un "FileStream". En ese caso, se simplifica la lectura de datos de 32 bits, a cambio de complicarse ligeramente la apertura y los "Seek", como se ve en este ejemplo:

/*---------------------------*/
/*  Ejemplo en C# nº 84:     */
/*  ejemplo84.cs             */
/*                           */
/*  Ficheros binarios (8):   */
/*  Información de un BMP    */
/*  con BinaryReader         */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
using System.IO;
 
public class Ejemplo84
{
 
  public static void Main()
  {
    BinaryReader fichero;
    string nombre;
    int compresion, ancho, alto;
    char marca1, marca2;
 
 
    Console.WriteLine("Comprobador de imágenes BMP\n");
    Console.WriteLine("Dime el nombre del fichero: ");
    nombre = Console.ReadLine();
 
    if (! File.Exists( nombre) )
    {
      Console.WriteLine("No encontrado!");
    }
    else
    {
      fichero = new BinaryReader(
        File.Open(nombre, FileMode.Open));
      // Leo los dos primeros bytes
      marca1 = Convert.ToChar( fichero.ReadByte() );
      marca2 = Convert.ToChar( fichero.ReadByte() );
 
      if ((marca1 =='B') && (marca2 =='M')) { // Si son BM
        Console.WriteLine("Marca del fichero: {0}{1}", 
          marca1, marca2);
 
        fichero.BaseStream.Seek(18, SeekOrigin.Begin);  // Posición 18: ancho        
        ancho = fichero.ReadInt32();
        Console.WriteLine("Ancho: {0}", ancho);
 
        alto = fichero.ReadInt32();
        Console.WriteLine("Alto: {0}", alto);
 
        fichero.BaseStream.Seek(4, SeekOrigin.Current); // 4 bytes después: compresión
        compresion = fichero.ReadInt32();
        fichero.Close();
 
        switch (compresion) {
          case 0: Console.WriteLine("Sin compresión"); break;
          case 1: Console.WriteLine("Compresión RLE 8 bits"); break;
          case 2: Console.WriteLine("Compresión RLE 4 bits"); break;
        }
      } else
        Console.WriteLine("No parece un fichero BMP\n");  // Si la marca no es BM
    }
  }  
}
 

Ejercicios propuestos: