[C] Modificar fichero

Vereis, según creo, si tienes un fichero y quieres modificarlo, tienes que leerlo completo cambiar lo que quieras dentro del array y volver a escribirlo entero.

¿Hay forma de ir a un punto del archivo (fseek) leer lo que se quiera (fread) cambiar el dato y volver a escribir en el fichero (???) sin tener que leer todo el fichero y escribir todo el fichero? Sería algo tipo sql pero con un archivo normal, no se bien como funciona el sql pero sería hacer algo tipo.

Busca: tornillo

Archivo:
alicates= 2;
llaves= 5;
tornillo= 28;

Sería... quizas este mal dicho, pasearse y buscar por el fichero como si se tratase de un array.

No se si se entiende o no ^^''

Saludos!
Si no me equivoco al utilizar fseek puedes alternar entre lectura y escriptura siempre y cuando el archivo lo hayas abierto en modo que permita esto. El truco es usar fseek pero sin desplazarte entonces podrás alternar el modo de lectura y escriptura sin abrir y cerrar el archivo

fseek ( pFile , 0 , SEEK_CUR );

No sé si es más o menos esto a lo que haces referencia.
Depende del tamaño de los datos que quieras modificar te servirá o no fseek. Me refiero, si es un archivo binario y quieres modificar valores enteros ("tipo int") no tendrás problemas ya que los datos ocuparán siempre 32 bits (depende de la arquitectura también, pero supongamos que no es un problema).

Si usas ficheros de texto, eso no es tan directo, dado que "128" ocupa 3 bytes, mientras que "28" ocupa solo 2 (un byte por carácter). Tendrás que "estandarizar" los datos, para que sepas que "llaves" tiene suficiente hueco para almacenar el nuevo valor, y no pisar a "tornillo". Lo más fácil es llevar registros de un tamaño fijo, es decir, que los nombres del registro siempre sean de 10 letras rellenando lo que sobre con espacios, y que el número sea siempre de 20 letras, rellenando también con espacios.

Así los accesos te serán mucho más fáciles, si quieres escribir la linea 10, solo tienes que hacer un fseek desde el inicio de 30(bytes por linea)*9 (linea en la que quieres empezar a escribir) +10 (nombre del registro 10). Lo mismo para la lectura. Me he comido los separadores tipo ":" y "\n", pero para el caso supongo que no te importará si no van a ser leidos.

Si vas a usar ficheros binarios, es el mismo caso, asegúrate de cuantos bytes reservas para el texto y el valor (puedes usar sizeof para saber el tamaño en bytes de cada campo) y así echas cuentas de donde te quieres posicionar en nº de bytes. Otra opción más elegante sería usar structs de tamaño fijo donde usas el sizeof del struct para echar cuentas y moverte por el fichero, leer almacenando directamente en el struct, volver a posicionarte en el fichero, y escribirlo. ASí usas siempre datos en binario, ahorrándote llamadas de "atoi" y viceversa cuando queires pasar un dato de un tipo a otro.
Si, básicamente lo que quiero es por ejemplo:

[utensilios]
//nombre, cantidad, precio
tornillo:23:29,5


[utensilios]
//nombre, cantidad, precio
tornillo:22:29,5


Poder cambiar el 23 por el 22 sin tener que copiar todo el archivo, cambiarlo, y volverlo a escribir, la cosa seria copiar la linea en la que esta no? Vuelvo a ponerlo asi porque no entiendo bien como darle ese uso a fseek.
¿Vas a trabajar con ficheros de texto o binarios?. Lo que tienes que entender es que cuando escribes, no es como en el word, no "añades". Sobreescribes lo que haya en esa posición, es como escribir con el botón "insert" activo. Ej:

[utensilios]
//nombre, cantidad, precio
tornillo:22:29,5


Ese fichero así tal cual, ocupa 56Bytes (un byte por caracter, contando los \n sin retorno de carro). El número "22" está en el byte 49 del fichero, por lo tanto puedes hacer un

fseek (fichero , 49 , SEEK_SET );
fputs ( "23" , fichero );

Y el resultado será:

[utensilios]
//nombre, cantidad, precio
tornillo:23:29,5


El problema viene cuando quieras cambiar el 23 por un 1300 (por ejemplo). Al hacer lo mismo lo que ocurrirá será:

fseek (fichero , 49 , SEEK_SET );
fputs ( "1300" , fichero );


[utensilios]
//nombre, cantidad, precio
tornillo:13009,5


Eso te sobreescribe los datos que ya tenías, solo podrás almacenar números del 00 al 99. Una solución es que todos los campos (nombre, cantidad y precio) tengan un tamaño prefijado, de "10 espacios" (10 bytes) cada uno por ejemplo. Así, cuando quieras editarlo, te asegurarás de que no haya problemas porque tendrás espacio. Ej:

[utensilios]\n
//nombre, cantidad, precio\n
tornillo  :23        :29,5          \n
tuerca    :100       :29,5          \n

He marcado los saltos de linea para que se vea que en el precio tienes 10 espacios.Ahora puedes hacer

fseek (fichero , 51 , SEEK_SET );
fputs ( "1300" , fichero );

Sin ningún peligro. Además, te será útil tener los tamaños prefijados para moverte dentro del fichero con fseek.
Un saludo, y suerte
Comprendo lo que dices, he trabajado con editores hexadecimales y si, mejor tener un tamaño prefijado.

Una pregunta, posiblemente tonta, ¿cómo hago un salto de línea en un fichero? ¿hago un fputs("\n",fichero)?
Sí, con eso bastaría para añadir una línea. El problema es que algunos editores (notepad solia usarlo) de texto usan "\r\n", que significa "retorno de carro + salto de linea". Asegurate de como se escriben los saltos de línea en el editor que uses, para seguir el mismo estandar. Aui tienes una explicacion un poco más extensa:

http://www.codeguru.com/forum/showthread.php?t=253826

Como ves, los estándares se los suelen pasar por el forro :), dependiendo de si estás en linux, windows o mac.
6 respuestas