[C con Clase] Trabajar con ficheros.

Steven Davidson steven en conclase.net
Jue Feb 22 21:05:42 CET 2007


Hola Daniel,

El pasado 2007-02-22 12:04:17, dani escribió:

d> Hola a todos,
d> Antes de nada y como siempre os agradezco toda la ayuda que me habéis
d> brindado hasta ahora.
d> Bueno, esta vex me pregunto si las funciones fscanf y fprinf tienen alguna
d> desventaja dado que en el curso C++ no se mencionan.

No. No tienen ninguna desventaja en cuanto a la funcionalidad. Hemos querido hablar de temas relacionados con C++, pero no confundirnos con C, especialmente para aquellas personas que nunca han visto C.

Personalmente, me parece más lioso usar la gama de funciones 'printf()' y 'scanf()' comparada con la jerarquía de 'iostream', aunque supongo que hay ventajas y desventajas para ambas implementaciones. Para 'printf()', tenemos que usar una cadena de formato, cuyo diseño no es tan fácil de entender a primera vista. El otro problema es que C/C++ no pueden comprobar si los tipos de cada parámetro son correctos o no. Por ejemplo,

char szCad[10];

printf( "%d %p %010f", szCad, szCad, szCad );

Como el diseño y lógica de 'printf()' es completamente "arbitrario", desde el punto de vista del lenguaje, el compilador no puede verificar si lo que estás haciendo está bien o no.

Con la clase 'iostream' y las demás en la jerarquía, no tenemos el problema anterior. La desventaja es que existen varias funciones miembros para las clases en dicha jerarquía, tanto si las usamos como si no. Otro problema es que forman parte de una plantilla, y por tanto nuestro ejecutable aumentará de tamaño.

Reiterando lo que dije, no existe ninguna desventaja si usas un diseño u otro. Eso sí, no se recomienda mezclar estas funciones de C con las clases de C++.

d> Por otra parte lo que si he encontrado en el curso C++ para trabajar con
d> ficheros es ifstream y ofstream sin embargo quiero hacer un programa que lea
d> un fichero de tamaño desconocido pero con una forma del tipo:
d>      k=1 m=1 blablabla
d>      'una matriz'
d>      k=1 m=2 blablabla
d>      'otra matriz'
d>      ...
d>      y así sucesivamente con posibles comentarios intercalados.
d> Y lo que quiero es que busque un k=? m=? determinado y lea la matriz. lo que
d> me ocurre es que en el curso lo que he encontrado para hacer las lecturas es
d> del tipo
d>      fe.getline(cadena,longitud);
d> es decir que necesito especificar la longitud de la cadena que en mi caso
d> desconozco y puede ser muy larga. ¿No se puede ir leyendo línea a línea?

Esto es exactamente lo que hace 'getline()': leer todos los caracteres hasta encontrarse con el carácter separador, que por defecto es '\n'. Claro que también se detiene si 'cadena' esté a punto de llenarse; o sea, lee 'longitud-1' caracteres.

El problema es que no sabemos cuántos caracteres existen en cada línea. He aquí la desventaja de usar ficheros de texto. Esto es parecido cuando usamos 'cin >>' para recibir una cadena de caracteres, la cual desconocemos si cantidad. Tenemos algunas posibles soluciones:

1. Buscar el fin de línea en el fichero. Esto implica que tenemos que ir carácter a carácter desde el fichero e ir contando la cantidad de caracteres que nos interesa tener. Para esto, podemos usar la función miembro 'get()'. Usando memoria dinámica, podemos crear nuestra cadena a medida. Por ejemplo,

ifstream ifs;
int nCant=0;
...
while( !ifs.eof() && ifs.get() != '\n' )  nCant++;

char *szLinea = new char[++nCant];

ifs.getline( szLinea, nCant );

2. Lo mismo que #1, pero en lugar de ir uno a uno, vamos recogiendo "bloques" e caracteres. Por ejemplo,

char szBufer[10240]="";  // 10 KB

do
{
  ifs.getline( szBuffer, 10240 );
  nCant += ifs.gcount();  // Cuántos caracteres hemos leído
} while( !ifs.eof() && ifs.gcount() >= 10240 )

Vamos yendo de bloque en bloque, en lugar de ir carácter a carácter.

3. Sugiero usar la clase 'string' para despreocuparte de la memoria dinámica. Por ejemplo,

string str;

getline( ifs, str );

Ahora la línea entera ha sido copiada a 'str'. La clase 'string' aumenta automáticamente su tamaño cuando sea necesario.


Esto es el problema de usar ficheros de texto. No sabemos las longitudes de la información contenida, y por tanto, las búsquedas son más lentas, porque hay que recorrer todo el fichero. Por esta razón es aconsejable leer todo el fichero, o cuanto puedas, en memoria. Así, colocamos toda la información en memoria de una forma organizada, y por tanto podemos acceder a ella directamente. Si esto no es plausible, entonces podrías crear una estructura que contenga las posiciones y longitudes de la información en el fichero. Podrías hacer una pasada completa del fichero al principio de tu programa para rellenar dicha estructura. Así no tienes que estar constantemente buscando en el fichero.

Claro está, podríamos usar un fichero en modo binario que suele ir bien con información de longitudes fijas.

Sugiero consultar el curso del manejo de ficheros en nuestra página. Puedes ir directamente a: http://c.conclase.net/ficheros/index.php


Espero que esto te ayude.

Steven


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