Bonjour,
mon problème est assez bête: j'ai un fichier composé par exemple de chiffres codés sur 1 octet:
0 1 2 3 4 5 6 7 8 9 10 0 0 11 12...
J'aimerais, sans avoir à créer un fichier temporaire, supprimer la séquence "0 0" (par exemple), afin d'obtenir 0 1 2 3 4 5 6 7 8 9 10 11 12...
Bien sûr, l'exemple est simpliste, le fichier ne contient pas ça, je ne veut pas forcément supprimer les 0, et les chiffres ne sont pas forcément codés sur 1 octet, mais l'idée y est.
En gros, j'aimerais supprimer n octets du fichier à partir de la position p.
J'ai regardé du coté de ftruncate, mais ça supprime tout à partir de p, et moi je veux supprimer seulement n octets
J'ai pensé à deux manières, mais je pense que ça risque d'être lent (si beaucoup de suppressions dans un gros fichier)
- créer un fichier temporaire, y copier tout ce qu'il y a avant p, sauter n octets, puis écrire le reste du fichier original dans le fichier temporaire, et enfin déplacer le fichier temporaire sur le fichier à rétrécire
- ne rien modifier avant p. Puis, à p+k , on place les données situées à p+n+k , puis enfin faire appel à ftruncate pour supprimer les n derniers octets du fichier
Je n'ai pas essayé, mais ça risque d'être très lent si je veux supprimer des milliers d'octets séparés par quelques octets... Quelqu'un aurait une idée plus rapide ?
Merci d'avance :)
# une ptite idee
Posté par David FRANCOIS (site web personnel) . Évalué à 0.
Enfin, je dis ca, je dis rien..
[^] # Re: une ptite idee
Posté par Moonz . Évalué à 1.
Réponse longue:
- Qu'est ce que tail a à faire là dedans ? Aux dernières nouvelles, il sert à obtenir la fin d'un fichier, et ce que je veux supprimer est en plein milieux (et accessoirement, tail ne fonctionne qu'avec des entiers signés sur un 1 octets - des char quoi - alors que le fichier en question est constitué par des entiers ou des flottants, signés comme non signés, et de taille allant de 1 à 4 octets)
- tail dans un prog en C ? je ne suis pas fan de system()... et man 2 tail me dit que ya pas de libtail :)
- je ne veux pas supprimer seulement la fin, sinon ce serait trop facile (et ftruncate serait suffisant)
Donc soit j'ai mal compris, soit tu as mal compris (ou les deux)
# AMHA
Posté par TheBreton . Évalué à 2.
tu ouvre ton fichier
tu fais un read de la taille N pour te positionner sur le 0 0 a supprimer (si tu ne sait pas ou il est dans le fichier je ne vois pas d'autre methodes que de les lires un par un)
tu fais un write dans un fichier temporaire de la taille N
tu fais un fseek (file_offset+2)
tu fais un read taillefichier-2-N
fwrite taillefichier-2-N
delete fichier
renome fichier temp en fichier
.
voila c'est grosso modo ce qui compte c'est ne pas lire UN par UN les octet du fichiers(ca c'est trèèèès lent) mais d'utiliser les fonctions gerant des buffers (ca c'est rapide)
[^] # Re: AMHA
Posté par Moonz . Évalué à 1.
Je pense pas que ce soit une bonne idée: mettons nous dans le pire des cas: je veux réduire 50 fois un fichier de 100 Mo (taille maximale je pense des fichiers que je veux manipuler). Dans ce pire des cas, je suis obligé de faire appel 50 fois à la fonction (ça, c'est lié à la structure de mon programme, et je ne peux pas changer ce comportement sans faire un hack immonde - et quitte à faire un hack dégueulasse, je préfère encore utiliser ma solution de rechange, qui gaspille de la place dans le fichier mais qui a le mérite d'être extrémement rapide). Donc je dois écrire 50*100 = 5000 = 5 Go sur le disque dur ! Et ça, c'est en comptant que déplacer le fichier ne recopie pas les données - si le répertoire /tmp est sur une partition différente on va devoir écrire 10 Go sur le disque... A contition que la partition /tmp soit assez grande, sinon le programme plante
De plus, soit j'utilise un buffer variable pour stocker les données lues par read (et dans ce cas si l'octet à supprimer est au début ou à la fin du fichier ça bouffera 100 Mo en RAM), soit un buffer constant (et dans ce cas je dois choisir une taille, trop grande, ça bouffe de la mem pour rien, trop petit je devrais faire beaucoup d'appels à read-write sur de petits buffer, et ça dégradera les performances)
Ma question, c'est s'il existe pas un moyen aussi simple pour réduire la taille d'un fichier que O_APPEND pour l'augmenter...
[^] # Re: AMHA
Posté par Florent C. . Évalué à 0.
0) Ouverture du fichier en lecture
1) Lecture des octets jusqu'à la position p dans un buffer b
2) fseek à p+n
3) Lecture des octets de p+n jusqu'à la fin dans b
4) Fermeture du fichier
5) Ouverture du fichier en écriture (écrasement)
6) Ecriture du buffer b dans le fichier, d'un seul coup, hop!
7) Fermeture du fichier
C'est quand même pas bien compliqué ...
[^] # Re: AMHA
Posté par Florent C. . Évalué à 0.
Dans ce cas, je te recommande toujours ma méthode, sauf que tu ne mets pas tout le fichier dans le buffer, mais en procédant par chunks ...
[^] # Re: AMHA
Posté par Moonz . Évalué à 1.
Pour l'encombrement mémoire, je suis d'accord que ce n'est pas forcément un problème, mais il reste le problème que dans le pire des cas je doit écrire 5 Go sur le disque dur :)
[^] # Re: AMHA
Posté par TheBreton . Évalué à 2.
Si tu fais apelle 50 fois a la fonction (pour 50 supression si je comprend bien) tu va creer 50 fichier temporaire de 100 Mo que tu renomeras 50 fois du meme nom a la fin donc ta conso disque est de 100Mo*2.(Ton fichier source et ton fichier temp qui remplace ton fichier source a la fin).
Le buffer variable en RAM peut etre reduit , par exemple rien ne t'empeche d'allouer 1Mo en RAM et de lire ton fichier par segment de 1Mo, dans ce cas une boucle while(EOF) fonctionnera tres bien.
En fait le plus efficace dans ce cas la est de prendre un buffer de la taille d'un inode, si ma memoire ne me joue pas de tour en ext2/3 ca doit faire 4Ko, prendre un petit buffer de 4Ko augmente la probabilité de ca presence en memoire physique et evite d'avoir du swap memoire sur le disk qui ralentit les I/O( pour moi un petit buffer mais judicieusement choisi ne degrade pas les performances).
Mias dans le contexte cité pour moi le seul moyen efficace c'est le hack immonde que tu cite.
Tu memoire dans ta fonction la liste des modifs dans un fichier (tes 50 appels) et ailleurs dans ton programme tu apelle une fonction qui les fait toutes d'un coup.
[^] # Re: AMHA
Posté par Moonz . Évalué à 1.
Un moyen serait de ne pas faire de fichier temporaire, et ainsi ce n'est pas la peine de réécrire tout ce qui est avant p (à partir de p on décale toutes les données de n octets vers le début). Si les données à supprimer sont vers la fin, ce sera rapide, mais si elles sont au début, on aura le même problème.. Je pense que je vais faire comme ça, sauf si c'est trop lent... Si ça intéresse quelqu'un, voilà ma fonction:
void fshrink(int fd, off_t start, size_t len) {
struct stat st;
size_t buf_size = COPY_BUFFER_SIZE, read_size;
uint8_t buf[buf_size];
lseek(fd, start, SEEK_SET);
do {
lseek(fd, len, SEEK_CUR);
read_size = read(fd, buf, buf_size);
lseek(fd, -len-read_size, SEEK_CUR);
write(fd, buf, read_size);
lseek(fd, buf_size, SEEK_CUR);
} while(read_size == buf_size);
fstat(fd, &st);
ftruncate(fd, st.st_size - len);
}
Bon, si ça marche pas, j'ai plus qu'à faire le "hack immonde" :(
# sed
Posté par Tony Ducrocq . Évalué à 1.
sed -i -e 's/0 0//' monfichier
le s pour remplacer (substitute), /0 0/ c'est la chaine à remplacer et // c'est ce par quoi il faut remplacer (rien quoi).
[^] # Re: sed
Posté par Tony Ducrocq . Évalué à 1.
# Réflexion...
Posté par liberforce (site web personnel) . Évalué à 2.
1. Tu lis le fichier d'origine (lecture seule)
2. Tu enregistre les positions des bouts à supprimer. Tu n'as qu'un seul fichier à gérer. Au pire, si tu dois le faire en plusieurs fois à cause de la structure de ton programme, tu concatènes tout dans ton fichier temporaire.
3. A la fin, tu fais une passe sur ton fichier d'origine tout en regardant les indices à éviter dans ton fichier temporaire et tu produis ton fichier résultat.
[...]
En y réfléchissant bien, je crois qu'une solution encore meilleure serait de faire cela au fil de l'eau à la première lecture du fichier, sans fichier temporaire, en sauvegardant dans un contexte ta position dans le fichier d'origine, pour savoir où reprendre dans ton traitement "morcelé"... Vu que de toute façon les écritures sont bufferisées, tu devrais pas avoir de performances trop dégueux... Par contre oublie pas de faire un fflush pour vider le cache disque et forcer l'écriture des données restantes... (Dites moi si je rconte des conneries, j'ai pas fait de C depuis un moment...)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.