Modelado logico relacional de bases de datos

Ficheros Binarios

Los ficheros binarios son una categoría de ficheros que almacenan datos en un formato binario, es decir, como secuencias de bits (ceros y unos), que no están directamente legibles por los humanos. Estos ficheros son más eficientes en términos de espacio y velocidad de procesamiento en comparación con los ficheros de texto, y se utilizan para almacenar datos complejos, como imágenes, vídeos, audios y programas ejecutables.

Estructura y manejo de ficheros binarios

  1. Estructura de los ficheros binarios:

    • En un fichero binario, los datos se almacenan directamente como secuencias de bits que representan diferentes tipos de datos: números, caracteres, imágenes, sonidos, etc. La representación binaria permite que los datos se almacenen de manera más compacta y eficiente en comparación con el formato de texto.
    • A diferencia de los ficheros de texto, en los cuales los datos se organizan por caracteres y líneas, los ficheros binarios pueden almacenar cualquier tipo de dato (primitivos, arrays, estructuras, objetos) en su forma binaria. Por ejemplo, un número entero, un flotante, o una imagen puede almacenarse sin necesidad de convertirlo a un formato legible como en el caso del texto.

    Estructura básica:

    • Cabecera (opcional): Puede contener información adicional, como el tipo de datos, el tamaño del fichero, o metadatos que ayuden a interpretar los datos del fichero.
    • Datos: Contiene la secuencia de bits que representan los datos almacenados.

    Ejemplo básico de almacenamiento binario:

    • Un número entero como 42 se representaría en binario como 00101010, mientras que el mismo número en un fichero de texto se representaría como los caracteres "4" y "2" (dos bytes en vez de uno solo).
  2. Manejo de ficheros binarios:

    • El manejo de ficheros binarios en la mayoría de lenguajes de programación requiere un enfoque ligeramente diferente al manejo de ficheros de texto. Dado que los datos no están estructurados en líneas o caracteres legibles, se debe especificar claramente cómo interpretar los bits almacenados.
    • En muchos lenguajes, el manejo de ficheros binarios implica abrir el fichero en modo binario (por ejemplo, utilizando el modo "rb" para leer o "wb" para escribir) y trabajar directamente con secuencias de bytes.

    Ejemplo en Python (lectura y escritura de ficheros binarios):

    • Lectura de un fichero binario:

      1with open('archivo.bin', 'rb') as file:
      2    data = file.read()
      3    print(data)  # Imprime los datos en formato binario
    • Escritura en un fichero binario:

      1data = b'\x42\x43\x44'  # Datos en binario
      2with open('archivo.bin', 'wb') as file:
      3    file.write(data)  # Escribe la secuencia binaria en el fichero

    En este ejemplo, se maneja el fichero como un flujo de bytes, y el contenido no es humanamente legible.

  3. Acceso aleatorio:

    • Otra ventaja clave de los ficheros binarios es que permiten un acceso aleatorio eficiente. Esto significa que puedes saltar a cualquier posición del fichero para leer o escribir datos, sin necesidad de procesar todo el contenido del fichero secuencialmente.
    • Esto es especialmente útil cuando trabajas con grandes cantidades de datos que no requieren ser leídos en secuencia, como en bases de datos o en grandes archivos multimedia.

    Ejemplo en Python (acceso aleatorio):

    1with open('archivo.bin', 'rb') as file:
    2    file.seek(10)  # Mueve el puntero a la posición 10
    3    data = file.read(4)  # Lee 4 bytes desde la posición 10
    4    print(data)

Diferencias con los ficheros de texto

Los ficheros binarios tienen varias diferencias significativas con respecto a los ficheros de texto, tanto en su estructura como en su manipulación:

  1. Formato:

    • Ficheros de texto: Almacenan datos en formato de texto legible por humanos, con cada carácter representado por un byte (en codificación ASCII) o varios bytes (en codificación UTF-8 o UTF-16). Las líneas de texto están delimitadas por caracteres especiales, como el salto de línea (\n).
    • Ficheros binarios: Almacenan datos en su forma binaria original, lo que permite una representación más eficiente de números, imágenes, sonidos, etc. Los datos no están organizados en líneas y no son legibles directamente.
  2. Eficiencia:

    • Ficheros de texto: Son menos eficientes en cuanto a espacio y velocidad de procesamiento, especialmente cuando se utilizan para almacenar datos numéricos o complejos, ya que estos datos deben ser convertidos a texto.
    • Ficheros binarios: Son más eficientes, ya que los datos se almacenan en su forma nativa, ocupando menos espacio y permitiendo un acceso y procesamiento más rápidos.
  3. Interpretación de datos:

    • Ficheros de texto: Almacenan datos que pueden interpretarse como texto legible, y cada línea suele estar separada por un delimitador (como un salto de línea). Por ejemplo, un número almacenado como texto debe ser convertido de una secuencia de caracteres ("42") a su valor numérico (42).
    • Ficheros binarios: Los datos son directamente interpretados como números, estructuras o secuencias de bytes. No hay necesidad de conversiones intermedias.
  4. Compatibilidad:

    • Ficheros de texto: Son portables entre diferentes sistemas, ya que pueden ser abiertos y editados en cualquier dispositivo que soporte la codificación utilizada (ej. UTF-8).
    • Ficheros binarios: Son menos portables, ya que requieren un programa específico que pueda interpretar la estructura de los datos.

Uso de ficheros binarios en diferentes contextos

Los ficheros binarios son esenciales en diversas aplicaciones donde se necesita almacenar y procesar grandes cantidades de datos, incluyendo:

  1. Multimedia (Imágenes, Sonido y Video):

    • Los ficheros multimedia como imágenes, audio y video suelen almacenarse en formatos binarios, ya que los datos binarios permiten una representación compacta y eficiente. Formatos como .jpg, .png, .mp3, .mp4, y .avi son ejemplos de ficheros binarios utilizados en aplicaciones multimedia.
    • Los archivos multimedia pueden contener grandes cantidades de datos, y el formato binario permite manejar estos datos sin sobrecargar el almacenamiento y el procesamiento. Además, estos archivos suelen utilizar algoritmos de compresión para reducir el tamaño del fichero, lo cual es crítico para el almacenamiento y la transmisión de datos multimedia.

    Ejemplo de uso: Un fichero de imagen en formato .jpg contiene datos binarios que representan los píxeles de la imagen, además de metadatos sobre la resolución, el formato de color, etc.

  2. Almacenamiento Comprimido:

    • Los ficheros binarios se utilizan en formatos de compresión, como .zip, .rar, y .gzip, donde se agrupan múltiples ficheros o datos y se reducen a un tamaño menor mediante algoritmos de compresión.
    • En estos ficheros, los datos se codifican en forma binaria para optimizar la eficiencia de almacenamiento y reducir el tamaño de los ficheros a enviar o almacenar.

    Ejemplo de uso: Al crear un archivo .zip, el contenido de uno o varios ficheros se comprime y almacena en un formato binario, lo que permite que los datos ocupen menos espacio en disco.

  3. Ejecutables y Programas:

    • Los programas ejecutables, como los archivos .exe en Windows o los binarios de Linux, son ficheros binarios que contienen el código máquina que puede ser interpretado y ejecutado directamente por el sistema operativo.
    • Estos ficheros no son legibles ni modificables fácilmente por los humanos y requieren programas específicos para ser creados, ejecutados o analizados.

    Ejemplo de uso: Un archivo .exe en Windows contiene código compilado que el sistema operativo puede ejecutar directamente.

  4. Bases de Datos:

    • Los sistemas de bases de datos almacenan grandes volúmenes de información en ficheros binarios que contienen registros, índices y metadatos. Estos ficheros permiten un acceso rápido y eficiente a los datos y están optimizados para su manipulación por parte de los motores de bases de datos.
    • Por ejemplo, las bases de datos como MySQL o PostgreSQL utilizan ficheros binarios para almacenar la información de las tablas y registros, permitiendo una gestión eficiente de los datos en términos de espacio y rendimiento.

Conclusión

Los ficheros binarios son esenciales en el almacenamiento y procesamiento de datos complejos en múltiples aplicaciones, desde multimedia hasta programas ejecutables. Su estructura y eficiencia los hacen fundamentales en contextos donde se necesita manejar grandes volúmenes de datos de manera rápida y optimizada. A diferencia de los ficheros de texto, los ficheros binarios ofrecen un manejo directo de datos en su formato nativo, lo que reduce el espacio de almacenamiento y mejora el rendimiento del sistema.

Tipos de ficheros binarios:

  • Ficheros binarios secuenciales
  • Ficheros binarios acceso directo / aleatorio
  • Ficheros binarios indexados

Ficheros Secuenciales

Los ficheros secuenciales almacenan los datos en el orden en que se van introduciendo. Es un método sencillo de organización de ficheros donde los registros se añaden de manera secuencial, uno tras otro, lo que los hace fáciles de escribir. Sin embargo, su principal desventaja es el acceso a los datos: para encontrar un registro específico, es necesario leer todos los registros anteriores, lo que puede hacer el acceso a registros más avanzados lento y poco eficiente.

Características y Limitaciones:

  1. Acceso secuencial:

    • Para acceder a un registro específico, el programa debe leer desde el principio del fichero y avanzar hasta encontrar el registro deseado.
    • Esto es eficiente si siempre necesitamos acceder a los primeros registros, pero se vuelve lento cuando hay que buscar registros en posiciones más avanzadas.
  2. Modos de apertura:

    • Los ficheros secuenciales solo permiten ser abiertos en modo de lectura o modo de escritura, dependiendo de la operación que se quiera realizar.
    • No se puede leer y escribir simultáneamente en el fichero secuencial; debe cerrarse y reabrirse en otro modo para realizar la operación inversa.
  3. Exclusividad de acceso:

    • Solo un usuario puede trabajar con el fichero a la vez, lo que limita su uso en entornos donde múltiples usuarios necesitan acceso concurrente.

Práctica: Fichero secuencial de proveedores

Queremos almacenar información sobre proveedores: su nombre, la persona responsable de la empresa y su número de teléfono. Los datos se irán añadiendo secuencialmente, uno tras otro.

Ejemplo de fichero secuencial:

Proveedor: ABC S.A. | Responsable: Juan Pérez | Teléfono: 555-1234
Proveedor: XYZ Ltd. | Responsable: María López | Teléfono: 555-5678
Proveedor: TechCorp | Responsable: Ana Torres | Teléfono: 555-9876

Ejemplo de fichero secuencial mejorado con sincronismo de registro:

Un fichero secuencial con sincronismo de registro puede implementar marcas o separadores para facilitar la identificación y lectura de cada registro.

START | Proveedor: ABC S.A. | Responsable: Juan Pérez | Teléfono: 555-1234 | END
START | Proveedor: XYZ Ltd. | Responsable: María López | Teléfono: 555-5678 | END
START | Proveedor: TechCorp | Responsable: Ana Torres | Teléfono: 555-9876 | END

Práctica: Creando y leyendo ficheros secuenciales con Python

  1. Crear un programa en Python que permita crear un fichero de proveedores:
1def crear_fichero_proveedores():
2    with open("proveedores.txt", "w") as file:
3        while True:
4            nombre = input("Nombre del proveedor: ")
5            responsable = input("Persona responsable: ")
6            telefono = input("Teléfono: ")
7            file.write(f"Proveedor: {nombre} | Responsable: {responsable} | Teléfono: {telefono}\n")
8            continuar = input("¿Desea agregar otro proveedor? (s/n): ")
9            if continuar.lower() != 's':
10                break
11
12crear_fichero_proveedores()
  1. Crear un programa en Python que permita leer el fichero de proveedores:
1def leer_fichero_proveedores():
2    with open("proveedores.txt", "r") as file:
3        for linea in file:
4            print(linea.strip())
5
6leer_fichero_proveedores()

Ejercicio: Escritura de un fichero secuencial

1# Escritura de un fichero secuencial
2clientes = [
3    "1. Ana López, Calle A, Madrid\n",
4    "2. Carlos Martínez, Calle B, Barcelona\n",
5    "3. José Pérez, Calle C, Sevilla\n",
6    "4. Laura García, Calle D, Valencia\n"
7]
8
9# Escribir los datos en un archivo secuencial
10with open('clientes_secuencial.txt', 'w', encoding='utf-8') as f:
11    f.writelines(clientes)

Ejercicio: Lectura secuencial para encontrar un cliente específico

1# Lectura secuencial para encontrar un cliente específico
2def encontrar_cliente_secuencial(nombre_buscado):
3    with open('clientes_secuencial.txt', 'r', encoding='utf-8') as f:
4        for linea in f:
5            if nombre_buscado.lower() in linea.lower():
6                print(f"Cliente encontrado: {linea.strip()}")
7                return
8    print("Cliente no encontrado.")
9
10# Buscar "Laura García"
11encontrar_cliente_secuencial("Laura García")

Ficheros de Acceso Directo / Aleatorio

Los ficheros de acceso directo permiten acceder a registros de manera instantánea, sin necesidad de leer los registros anteriores. Esto es posible porque cada registro tiene una longitud fija, lo que permite calcular la posición de cualquier registro en el fichero. Es muy eficiente para recuperar registros específicos de grandes volúmenes de datos.

Características y Limitaciones:

  1. Acceso rápido:

    • Permite acceder directamente a un registro calculando su posición en el fichero, sin necesidad de leer los registros anteriores.
  2. Longitud fija de registros:

    • Cada registro debe ocupar el mismo número de bytes. Si un dato no llena todo el espacio asignado, se deja el espacio sobrante vacío, lo que puede causar desperdicio de espacio.
  3. Apertura mixta:

    • Estos ficheros permiten apertura mixta, es decir, pueden ser abiertos en modo de lectura y escritura al mismo tiempo, lo que facilita la actualización de registros.
  4. Acceso simultáneo:

    • Varios usuarios pueden trabajar sobre el fichero simultáneamente sin interferencia, lo que es una ventaja sobre los ficheros secuenciales.

Práctica: Creando un fichero de acceso directo

Imaginemos que ahora queremos guardar los datos de proveedores con una longitud fija para cada campo:

  • Nombre del proveedor: 30 bytes
  • Nombre del contacto: 30 bytes
  • Teléfono: 9 bytes
  • Total por registro: 69 bytes

Si deseamos acceder al registro 777, podemos calcular su posición fácilmente:

(777 - 1) * 69 = 53,544 bytes

Este es el byte exacto donde comienza el registro 777 en el fichero, lo que permite un acceso inmediato.


Ejercicio: Escritura en un fichero binario de acceso directo

1import struct
2
3# Definir el formato del registro: ID (int), nombre (20s), precio (float), stock (bool)
4# 'i' = int, '20s' = cadena de 20 bytes, 'f' = float, '?' = bool
5formato = 'i20sfi'
6tamaño_registro = struct.calcsize(formato)
7
8# Datos de productos
9productos = [
10    (123, b"Laptop", 1000.0, True),
11    (456, b"Mouse", 25.0, False),
12    (789, b"Teclado", 50.0, True)
13]
14
15# Escritura en un fichero binario de acceso directo
16with open('productos_directo.dat', 'wb') as f:
17    for producto in productos:
18        # Asegurarse de que el nombre tiene exactamente 20 bytes
19        nombre_padded = producto[1].ljust(20, b' ')
20        f.write(struct.pack(formato, producto[0], nombre_padded, producto[2], producto[3]))

Ejercicio: Leer el producto en la posición 1 (Segundo producto)

1import struct
2
3# Definir el formato del registro: ID (int), nombre (20s), precio (float), stock (bool)
4# 'i' = int, '20s' = cadena de 20 bytes, 'f' = float, '?' = bool
5formato = 'i20sfi'
6tamaño_registro = struct.calcsize(formato)
7
8# Función para leer un producto por su posición (0-based)
9def leer_producto(posicion):
10    with open('productos_directo.dat', 'rb') as f:
11        f.seek(posicion * tamaño_registro)
12        datos = f.read(tamaño_registro)
13        if not datos:
14            print("Posición fuera de rango.")
15            return
16        id_producto, nombre, precio, stock = struct.unpack(formato, datos)
17        nombre = nombre.decode('utf-8').strip()
18        print(f"Producto en posición {posicion}: ID={id_producto}, Nombre={nombre}, Precio={precio}, Stock={stock}")
19
20# Leer el producto en la posición 1 (Segundo producto)
21leer_producto(1)

Ficheros Indexados

El acceso indexado es una técnica que surge para corregir las ineficiencias tanto de los ficheros secuenciales como de los de acceso directo. Un fichero indexado utiliza un índice que permite acceder directamente a los registros. Este índice actúa como un mapa que indica dónde se encuentra cada registro dentro del fichero, permitiendo la búsqueda eficiente sin necesidad de leer todos los registros anteriores.

Características:

  1. Índice de búsqueda:

    • El índice contiene referencias a la posición de los registros dentro del fichero. Esto hace que sea mucho más rápido encontrar un registro sin necesidad de conocer su posición exacta o de recorrer el fichero secuencialmente.
  2. Eficiencia en la búsqueda:

    • Se mejora el rendimiento en la búsqueda de datos, ya que solo es necesario consultar el índice, que es mucho más pequeño que el conjunto de datos en sí.
  3. Ideal para bases de datos:

    • Los ficheros indexados son una estructura base en sistemas de bases de datos, donde la velocidad de acceso a registros específicos es crucial.

Ejemplo:

Imaginemos que tenemos un fichero con los nombres de los empleados y sus identificadores. En lugar de buscar secuencialmente el identificador de cada empleado, un índice nos dice dónde se encuentra cada identificador en el fichero, permitiendo acceso inmediato.


Ejercicio: Escritura en un fichero binario de acceso directo

1import struct
2import pickle
3
4# Definir el formato del registro: DNI (10s), nombre (20s), puesto (15s), fecha contratación (10s)
5formato = '10s20s15s10s'
6tamaño_registro = struct.calcsize(formato)
7
8# Datos de empleados
9empleados = [
10    (b'45678901X', b"Juan", b"Gerente", b"01/01/2020"),
11    (b'78901234A', b"Maria", b"Ingeniera", b"15/03/2021"),
12    (b'12345678B', b"Pedro", b"Analista", b"20/06/2019")
13]
14
15# Crear un índice: DNI -> posición
16indice = {}
17
18# Escritura en un fichero binario y creación del índice
19with open('empleados_indexado.dat', 'wb') as f:
20    for pos, empleado in enumerate(empleados):
21        f.write(struct.pack(formato, *empleado))
22        indice[empleado[0].decode('utf-8')] = pos
23
24# Guardar el índice en un archivo separado
25with open('indice_empleados.pkl', 'wb') as f:
26    pickle.dump(indice, f)

Ejercicio: Leer el producto en la posición 1 (Segundo producto)

1import struct
2import pickle
3
4# Definir el formato del registro: DNI (10s), nombre (20s), puesto (15s), fecha contratación (10s)
5formato = '10s20s15s10s'
6tamaño_registro = struct.calcsize(formato)
7
8
9# Función para leer un empleado por su DNI usando el índice
10def leer_empleado_por_dni(dni_buscado):
11    # Cargar el índice
12    with open('indice_empleados.pkl', 'rb') as f:
13        indice = pickle.load(f)
14    
15    pos = indice.get(dni_buscado)
16    print(pos)
17    if pos is None:
18        print("Empleado no encontrado.")
19        return
20    
21    with open('empleados_indexado.dat', 'rb') as f:
22        f.seek(pos * tamaño_registro)
23        datos = f.read(tamaño_registro)
24        dni, nombre, puesto, fecha = struct.unpack(formato, datos)
25        dni = dni.decode('utf-8').strip()
26        nombre = nombre.decode('utf-8').strip()
27        puesto = puesto.decode('utf-8').strip()
28        fecha = fecha.decode('utf-8').strip()
29        print(f"Empleado encontrado: DNI={dni}, Nombre={nombre}, Puesto={puesto}, FechaContratación={fecha}")
30
31# Buscar un empleado por DNI
32leer_empleado_por_dni("12345678B")

Ejercicio: Leer fichero bmp

1import struct
2
3def leer_bmp(archivo_bmp):
4    with open(archivo_bmp, 'rb') as f:
5        # Leer cabecera BMP (14 bytes)
6        cabecera_bmp = f.read(14)
7        bm, tamano_archivo, reservado1, reservado2, offset_datos = struct.unpack('<2sIHHI', cabecera_bmp)
8
9        # Mostrar información de la cabecera BMP
10        print("Cabecera BMP")
11        print(f"Tipo de archivo: {bm.decode('utf-8')}")
12        print(f"Tamaño del archivo: {tamano_archivo} bytes")
13        print(f"Reservado1: {reservado1}")
14        print(f"Reservado2: {reservado2}")
15        print(f"Offset de los datos de la imagen: {offset_datos} bytes\n")
16
17        # Leer la cabecera DIB (40 bytes para BITMAPINFOHEADER)
18        cabecera_dib = f.read(40)
19        tamano_dib, ancho, alto, planos, bits_por_pixel, compresion, tamano_imagen, resolucion_horizontal, resolucion_vertical, colores, colores_importantes = struct.unpack('<IiiHHIIIIII', cabecera_dib)
20
21        # Mostrar información de la cabecera DIB
22        print("Cabecera DIB (BITMAPINFOHEADER)")
23        print(f"Tamaño de la cabecera DIB: {tamano_dib} bytes")
24        print(f"Ancho: {ancho} píxeles")
25        print(f"Alto: {alto} píxeles")
26        print(f"Planos de color: {planos}")
27        print(f"Bits por píxel: {bits_por_pixel}")
28        print(f"Compresión: {compresion}")
29        print(f"Tamaño de los datos de la imagen: {tamano_imagen} bytes")
30        print(f"Resolución horizontal: {resolucion_horizontal} píxeles por metro")
31        print(f"Resolución vertical: {resolucion_vertical} píxeles por metro")
32        print(f"Colores en la paleta: {colores}")
33        print(f"Colores importantes: {colores_importantes}\n")
34
35        # Saltar a la posición de los datos de la imagen
36        f.seek(offset_datos)
37
38        # Leer los primeros 16 bytes de los datos de la imagen para mostrar un ejemplo (opcional)
39        datos_imagen = f.read(16)
40        print(f"Primeros 16 bytes de los datos de la imagen: {datos_imagen}")
41
42# Llamar a la función con el nombre del archivo BMP
43archivo_bmp = 'fotoBlancoYNegro.bmp'  # Reemplaza con la ruta a tu archivo BMP
44leer_bmp(archivo_bmp)

Conclusión

Cada tipo de fichero tiene sus propias ventajas y limitaciones. Los ficheros secuenciales son simples pero lentos para búsquedas avanzadas. Los ficheros de acceso directo permiten acceso rápido pero requieren longitud fija en los registros. Los ficheros indexados resuelven muchos de los problemas de los otros dos tipos mediante el uso de índices, haciendo que la búsqueda de datos sea mucho más eficiente.

  • Loading...