[C con Clase] Archivos binarios vs archivos de texto

Salvador Pozo salvador en conclase.net
Vie Mar 30 00:43:31 CEST 2007


El pasado 2007-03-29 20:36:42, Javier Sánchez Martínez escribió:

Hola:
 
JSM> Una gran verdad la distinción.... bajo mi opinion hay que tener en cuenta
JSM> (pienso yo) que en aplicaciones tales como bases de datos es mucho mas
JSM> facil     el tratamiento de ficheros binarios con estructuras de datos...
JSM> se are el fichero y se copian los primeros x bytes en la posición de
JSM> memoria de una estructura o a la inversa... es mucho mas sencillo el
JSM> guardado y la obtención de datos... siempre siquiendo un patron... (algo
JSM> tipo la cabecera de los ficheros ejecutables) PE por ej...

Todo es relativo, dependerá de cada caso, de cada problema, del número de datos y del tamaño de cada uno, de la disparidad o rangos de posibles valores (dominios), de las optimizaciones que podamos aplicar, etc

Debemos intentar ser flexibles en las soluciones, porque el tipo de problemas que debemos resolver es muy amplio, y las situaciones imprevisibles.

JSM> x cierto una preunta.... que querias decir con "los ficheros con tamaño de
JSM> registro constante son menos eficientes que los de tamaño
JSM> variable" con tamaño de registro te refieres a tamaño de caracter
JSM> dependiendo de la charset usada UTF8 etc... ?? oa ke ???

No, no me refiero a eso.

Generalmente, los ficheros que contienen tablas o conjuntos de datos del mismo tipo, como los registros de una base de datos o un array de estructuras, están compuestos por conjuntos de datos coherentes, todos del mismo tamaño, independientemente del tamaño de cada dato dentro del registro.

Así, por ejemplo, como mencionaba Joaquín en su mensaje, si uno de los datos miembro de la estructura es un array de 100 caracteres, siempre se almacenan 100 caracteres, independientemente de que la cadena que contenga cada elemento del array tenga más o menos caracteres válidos.

Esto pasa con los arrays en memoria, y si los guardamos en disco directamente, también pasa con los ficheros de datos binarios.

Sin embargo, no estamos obligados a hacer esto, en ninguno de los dos casos.

Lo explicaré con un ejemplo. Supongamos que tenemos un array con los nombres y apellidos de varias personas. En nuestra primera aproximación podemos dar un tamaño fijo a cada parte de la estructura:

struct persona {
   char nombre[64];
   char apellidos[128];
} tabla[50];

Está claro que si almacenamos el nombre de "Ana Pi" estaremos desperdiciando mucha memoria, 60 bytes para el nombre y 125 para los apellidos.

Pero podemos optar por otra estructura:

struct persona {
   char *nombre;
   char *apellidos;
} tabla[50];

Esta estructura nos dará más trabajo, ya que estaremos obligados a reservar memoria dinámica para cada nombre y apellidos. La ventaja es que optimizaremos el uso de la memoria, gastando mucha menos.

Con los ficheros en disco podemos hacer lo mismo.

En los ficheros de texto se suele usar una línea para cada registro, usando delimitadores, como comas, punto y coma, o tabuladores. Podemos guardar ficheros con tamaño de registro constante, que sería lo equivalente al primer caso:

Ana<--- 60 espacios --->;Pi<---  125 espacios   --->
Pedro<-- 58 espacios -->;Gonzalez <--120 espacios-->
etc...

O podemos optar por una técnica más económica, eliminado los espacios no usados:

Ana;Pi
Pedro;Gonzalez
etc...

En este caso, los registros son de tamaño variable.

La desventaja de esta segunda técnica es que no es tan fácil hacer un acceso aleatorio. El acceso aleatorio nos permite acceder a cualquier registro del fichero mediante un índice. Si todos los registros tienen la misma longitud, localizar uno concreto es tan fácil como hacer una multiplicación. Pero si cada registro tiene una longitud diferente, localizar uno concreto nos obliga a una lectura secuencial.

La solución, en este caso, también es posible. Al igual que pasa con nuestro array del segundo ejemplo, acceder a un dato concreto es más sencillo si se organiza el fichero de la forma adecuada.

Hay muchas formas de solucionar esto, pero comentaré dos:

1) Añadir un bloque con punteros a cada registro. En un fichero, el concepto de puntero es un desplazamiento:
[punteros]
0;
7;
22;
...
[datos]
Ana;Pi
Pedro;Gonzalez
etc...

Ahora, si quiero leer el segundo registro, leo el puntero en la segunda línea de punteros, y con ese deplazamiento leo el dato en la línea correspondiente.

Por supuesto, hará falta algo más de información, como la posición donde empieza la tabla de datos. También ayudaría que en la tabla de punteros los registros sí fuesen de longitud constante.

2) Hacer algo similar a lo que hacemos en memoria: guardar los punteros a las cadenas, como desplazamientos en el fichero:

[Registros]
0;4
7;13
...
[cadenas]
Ana
Pi
Pedro
Gonzalez
...
etc...

En este caso, también ayuda que el tamaño de los registros de la zona de registros sea fijo.

Sin embargo, toda esta optimización de espacio tiene un precio.

Por una parte, hay una mayor complejidad en el código, requerirá más tiempo de desarrollo, y más tiempo de procesador.

Por otra parte, los ficheros resultantes ya no son tan fácilmente editables a mano, aunque sigan siendo legibles. Si modificamos un dato, tendremos que calcular los nuevos desplazamientos, lo que en definitiva, complica demasiado la edición.

Hasta pronto.

-- 
Salvador Pozo (Administrador)
mailto:salvador en conclase.net


Más información sobre la lista de distribución Cconclase