Archivos (7): fread, fwrite y los arrays

Similar a la primitiva fwrite, en este vídeo vamos a ver el funcionamiento de la primitiva fread, para leer el contenido de un archivo y cargarlo de forma binaria en un buffer de memoria. También vemos cómo volcar un array de estructuras usando el tercer parámetro de las primitivas fread y fwrite.

Cuando trabajamos con archivos binarios en C, una de las funciones clave para cargar datos en memoria es fread. Esta función nos permite leer bloques de bytes desde un archivo y almacenarlos directamente en una zona de memoria que especifiquemos, lo que resulta especialmente útil cuando manejamos estructuras de datos complejas.

Imaginemos que tenemos una estructura llamada medición que contiene varios campos, como fecha, temperatura, índice ultravioleta y viento. Si declaramos una variable local de tipo medición sin inicializarla, su contenido será basura, ya que contendrá lo que haya en la pila en ese momento. Por eso, si imprimimos sus valores antes de cargar datos, veremos valores incoherentes o aleatorios.

Para cargar los datos guardados previamente en un archivo binario, utilizamos fread. Esta función requiere que le pasemos un puntero al buffer donde queremos almacenar los datos, el tamaño de cada elemento que queremos leer (usualmente el tamaño de nuestra estructura), el número de elementos que deseamos leer y el puntero al archivo abierto en modo lectura binaria ("rb"). Por ejemplo, si queremos leer una sola estructura medición desde un archivo llamado temperatura.bin, abriríamos el archivo con fopen("temperatura.bin", "rb") y luego llamaríamos a fread pasando la dirección de nuestra variable medida, el tamaño de la estructura, el número 1 y el puntero al archivo.

Es importante abrir el archivo en modo binario para evitar problemas en sistemas Windows, donde los caracteres de salto de línea pueden ser interpretados y modificados, lo que corrompería los datos binarios. La función fread devuelve el número de elementos leídos correctamente, por lo que siempre conviene comprobar que este valor coincide con el número esperado para asegurarnos de que la lectura fue exitosa.

Una vez que hemos leído los datos, podemos imprimirlos y veremos que ahora contienen la información almacenada en el archivo, ya que fread ha copiado literalmente los bytes desde el archivo a la memoria, y la estructura nos permite interpretar esos bytes correctamente.

Cuando trabajamos con múltiples registros, podemos manejar arrays de estructuras. Por ejemplo, si declaramos un array medidas de tipo struct medición con 5 elementos, podemos inicializar cada uno con diferentes valores. Para guardar este array en un archivo, usamos fwrite pasando el puntero al inicio del array, el tamaño de la estructura y el número de elementos (5 en este caso). Esto escribirá consecutivamente las 5 estructuras en el archivo.

Al leer, hacemos algo similar con fread: abrimos el archivo en modo lectura binaria, y leemos 5 elementos directamente en el array medidas. Luego, podemos recorrer el array con un bucle para imprimir cada registro y verificar que la lectura fue correcta.

Un detalle interesante es que el tercer parámetro de fread y fwrite (el número de elementos) y el segundo parámetro (el tamaño de cada elemento) se multiplican para determinar la cantidad total de bytes transferidos. Por eso, podemos intercambiar estas dos cifras siempre que el producto sea el mismo, por ejemplo, leer 5 elementos de tamaño sizeof(struct medición) o 1 elemento de tamaño 5 * sizeof(struct medición).

Sin embargo, debemos tener cuidado si nuestras estructuras contienen punteros. En ese caso, al escribir y leer con fwrite y fread, solo se almacenan y recuperan las direcciones de memoria, no el contenido al que apuntan esos punteros. Esto puede provocar errores o corrupción de datos, ya que las direcciones de memoria no serán válidas al volver a cargar los datos. Por eso, cuando trabajamos con estructuras que contienen punteros, es necesario implementar mecanismos adicionales para serializar y deserializar correctamente el contenido apuntado.

En resumen, fread es una herramienta poderosa para cargar datos binarios en memoria, especialmente cuando trabajamos con arrays de estructuras. Nos permite recuperar el estado guardado en archivos y continuar trabajando con esos datos en nuestros programas, siempre que tengamos en cuenta las precauciones necesarias con el manejo de punteros y el tamaño de los datos que leemos.

Lista de reproducción
  1. 1
    Instalar CodeBlocks
    15 minutos
  2. 2
    Funciones y hola mundo
    17 minutos
  3. 3
    Variables y tipos de datos
    17 minutos
  4. 4
    Condicionales y operadores lógicos
    16 minutos
  5. 5
    Bucles
    11 minutos
  6. 6
    Punteros
    12 minutos
  7. 7
    Arrays
    14 minutos
  8. 8
    Estructuras
    12 minutos
  9. 9
    Otras construcciones de C
    9 minutos
  10. 10
    Memoria dinámica
    8 minutos
  11. 11
    El preprocesador (parte 1)
    10 minutos
  12. 12
    El preprocesador (parte 2)
    9 minutos
  13. 13
    Archivos de cabecera y múltiples .c (parte 1)
    8 minutos
  14. 14
    Archivos de cabecera y múltiples .c (parte 2)
    9 minutos
  15. 15
    C desde la línea de comandos (parte 1)
    8 minutos
  16. 16
    C desde la línea de comandos (parte 2)
    9 minutos
  17. 17
    Break y continue
    10 minutos
  18. 18
    Goto
    13 minutos
  19. 19
    Manipulación de bits
    15 minutos
  20. 20
    Máscaras de bit
    19 minutos
  21. 21
    Archivos (1): fopen y fclose
    13 minutos
  22. 22
    Archivos (2): leer con fgetc
    9 minutos
  23. 23
    Archivos (3): fseek y ftell
    11 minutos
  24. 24
    Archivos (4): leer con fgets
    9 minutos
  25. 25
    Archivos (5): fputc y fputs
    7 minutos
  26. 26
    Archivos (6): volcar en archivos con fwrite
    10 minutos
  27. 27
    Archivos (7): fread, fwrite y los arrays
    10 minutos
  28. 28
    Archivos (8): entrada estándar y salida estándar
    9 minutos
  29. 29
    Archivos (9): buffers
    14 minutos
  30. 30
    Archivos (y 10): otras funciones útiles con archivos
    5 minutos
  31. 31
    printf (1)
    18 minutos
  32. 32
    printf (parte 2)
    12 minutos
  33. 33
    scanf (parte 1)
    17 minutos
  34. 34
    scanf (parte 2)
    17 minutos
  35. 35
    fprintf, sprintf y snprintf
    8 minutos
  36. 36
    Tipos de datos opacos
    13 minutos
  37. 37
    Bibliotecas estáticas
    13 minutos
  38. 38
    Bibliotecas dinámicas
    15 minutos
  39. 39
    Más flags: i mayúscula (include), wall, werror, pedantic...
    12 minutos
  40. 40
    pkg-config
    12 minutos
  41. 41
    Make
    17 minutos
  42. 42
    GDB
    21 minutos
  43. 43
    Variables globales
    6 minutos
  44. 44
    extern
    9 minutos
  45. 45
    Funciones variádicas
    12 minutos
  46. 46
    El optimizador de GCC y la opción -O
    12 minutos
  47. 47
    Volatile
    6 minutos