Supprimer derniere ligne d'un fichier SANS le parcourir

Supprimer derniere ligne d'un fichier SANS le parcourir - C - Programmation

Marsh Posté le 27-08-2016 à 12:28:10    

Bonjour tout le monde ,  :hello:  
 
Je recherche une solution afin de pouvoir supprimer la dernière ligne d'un fichier (.txt ou autre) en C. Le problème est que je ne connais par avance, ni la taille du fichier, ni la longueur de la dernière ligne... Le fichier pouvant faire plusieurs Go, il est hors de question de parcourir chaque lignes jusqu’à la dernière...
 
Du coup je m’étais dis que je pourrais peut-être me placer directement à la fin du fichier (avec fseek() ) puis remonter jusqu'au dernier '\n' et écraser tout ce qui suit avec des " " ( du style : fprinf(monFichier,"                                                                                                                                                      " );     ). C'est un peu "sale" mais bon... :D
 
Comment puis-me placer au début de la dernière ligne ?
Connaissez-vous une façon moins "sale" de supprimer la dernière ligne sans parcourir tout le fichier ?
 
Merci pour votre aide :)

Reply

Marsh Posté le 27-08-2016 à 12:28:10   

Reply

Marsh Posté le 27-08-2016 à 14:13:00    

Si tu sais que ta dernière ligne n’excède pas une certaine longueur K, tu peux te positionner a K octets de la fin, copier les K derniers octets de ton fichier dans un buffer, y calculer la position du dernier \n et en déduire à quelle longueur tu dois tronquer ton fichier.
Ça suppose néanmoins que fseek positionné par rapport à la fin ne parcourt pas tous le fichier pour y aller, ce qui peut ne pas être le cas si ton implem de fseek n'est pas optimisée.
 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 27-08-2016 à 14:36:30    

Merci pour votre réponse :)
 
Je viens de faire le code en C et ca fonctionne nikel. J'en ai profité pour récupérer la dernière ligne avant de la supprimer ^^

Reply

Marsh Posté le 27-08-2016 à 14:52:27    

Faut faire juste gaffe au cas limite du fichier de taille < K
Comme de toute façon tu connais la taille du fichier, c'est pas couteux de tester.
 
A+,


Message édité par gilou le 27-08-2016 à 14:53:24

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 27-08-2016 à 15:55:06    

Bon... Ben en faite, je ne sais pas pourquoi mais j'ai une erreur ( 0x0000005) avec mon code :cry: . Le printf("Debug\n" ); indique le dernier moment avant le bug. Le printf("Debug\n" ); indique celui qui ne s'affiche pas. Donc visiblement l'erreur arrive au niveau ma boucle "do...While"...
 
Je répète que je ne connais pas la taille de mon fichier, ni son nombre de lignes, ni la taille de la dernière ligne ^^
 
voici mon code :
 

Code :
  1. long position = 0;                                             
  2.                 long DebutDerniereLigne = 0;                                             
  3.                 static const long max_len = 128 + 1;   
  4.                 char buff[max_len + 1];                                     
  5.                 fseek(fichier, -max_len, SEEK_END);
  6.                 fread(buff,max_len-1,1,fichier);
  7.                 buff[max_len-1] = '\0';
  8.                 char *last_newline = strrchr(buff, '\n');
  9.                 char *last_line = last_newline+1;
  10.                 char derniereLigne[128] = "";                 
  11.                 int i =0;
  12.                 printf("debug\n" );
  13.                 do
  14.                 {
  15.                     //printf("%d\n",i);
  16.                     derniereLigne[i] = last_line[i];
  17.                     i++;
  18.                 }while(last_line[i] != '\0');
  19.                 printf("debug\n" );


 
Je ne comprends pas comment je peux avoir une erreur d'accès mémoire... J'ai testé avec "while...do" et  while(last_line[i-1] != '\0');   ou   while(last_line[i+1] != '\0'); et c'est exactement pareil, mon printf("%d\n",i); écrit 0, puis ca plante...
 
Qqun a-t'il une solution ? Je comprends pas la :heink:  
 
Merci pour votre aide

Reply

Marsh Posté le 27-08-2016 à 17:47:18    

Tu l'ouvres comment ton fichier? Sous Windows en tout cas je dois mettre fopen(..., "rb" ). Après il y a des +1/-1 en trop/inutiles. Par contre si le fichier est trop court --> BOUM. Un peu de gestion d'erreur (fseek, fread, strrchr) ne serait pas une mauvaise idée.
 
Code sur demande.

Reply

Marsh Posté le 27-08-2016 à 22:50:09    

Effectivement, en ouvrant le fichier en "rb+" c'est mieux  :jap:  
J'ai fini par y arriver en utilisant une méthode un peu "dégeux" ^^, mais c'est toujours mieux que de parcourir encore et encore tout un fichier de plusieurs dizaines de Go :)
 
Voici mon code ( qui fonctionne bien, en tous cas j'ai pas encore eu d'erreur :whistle: et je l'ai testé sur plusieurs fichiers de plusieurs formats et différentes tailles )  :
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. int main()
  5. {
  6.     FILE* fichier = NULL;
  7.     fichier = fopen("AEF-2015.pdf","rb+" );
  8.     if(fichier != NULL)
  9.     {
  10.         printf("Le fichier est ouvert en rb+...\n" );
  11.         fseek(fichier,-1,SEEK_END);                           // Je me place a l'avant-dernier caractère du fichier pour éviter de tomber                                                                    //directement sur EOF dans ma prochaine boucle
  12.         int caractereActuel =0;
  13.         int i =0;
  14.         do
  15.         {
  16.             caractereActuel = fgetc(fichier);
  17.             i++;
  18.             fseek(fichier,-2,SEEK_CUR);
  19.         } while (caractereActuel != '\n');                      // je parcours mon fichier a l'envert en partant de la fin et je compte la                                                                    //taille de ma derniere ligne => jusqu'a tomber sur un '\n'.
  20.         int c = 0;
  21.         char tab[512] = "";
  22.         for(c=0;c<i+1;c++)                          // A partir du début de ma dernière ligne, je récupère autemps de caractères que la                                                              //taille de ma ligne => toute ma derniere ligne sans le EOF
  23.         {
  24.             tab[c] = fgetc(fichier);
  25.         }
  26.         fseek(fichier,-i,SEEK_CUR);               // Je me replace au début de ma dernière ligne
  27.         do
  28.         {
  29.             fputc(' ',fichier);                  // Je re-parcours ma dernière ligne en remplaçant tous les caractères par des 'ESPACE'                                                             //(ici j'aurai préféré la supprimer mais bon...c'est mieu que rien XD)
  30.             i--;
  31.         } while (i != 0);
  32.         printf("La Derniere ligne est : %s\n",tab);       // Et voila ! tab contient ma derniere ligne et ma derniere ligne a disparut !
  33.         fclose(fichier);
  34.         printf("Le fichier est ferme...\n" );
  35.     }
  36.     else
  37.     {
  38.         printf("Le fichier est introuvable...\n" );
  39.     }
  40.     return 0;
  41. }


 
Voila, au final ce programme fait a peu prés ce que je voulais, cad : Lire la dernière ligne, et la supprimer (la faire disparaître visuellement)  :sol: . Bien sur, si je pouvais tout simplement supprimer la dernière ligne ce serait mieux, mais bon, c'est déjà bien comme ca.
 
Bonne soirée a tous  :hello:

Reply

Marsh Posté le 12-09-2016 à 01:33:00    

Tu pourrais utiliser un buffer correspondant a la taille de ton fichier,  et récupérer l'avant dernier \n et delete tout entre le \n et la fin de ton buffer

Reply

Marsh Posté le 12-09-2016 à 10:25:55    

suiL a écrit :

Tu pourrais utiliser un buffer correspondant a la taille de ton fichier,  et récupérer l'avant dernier \n et delete tout entre le \n et la fin de ton buffer


Si le fichier fait plusieurs dizaines de Go c'est pas trop une option. Sur plateforme 64b tu peux mmapper le fichier par contre, c'est à ça que ça sert. Et tu voudras dans tous les cas appliquer le même processus d'itérer les caractères en partant de la fin histoire de pas te taper 10Go de truc dont tu te fous, donc ça va pas faire une grosse différence.

 

Si les lignes sont très très longues tu peux charger tes données dans un petit buffer (16 octets) pour pouvoir vectoriser la recherche, mais bon c'est beaucoup de complexité pour probablement pas grand chose.


Message édité par masklinn le 12-09-2016 à 10:31:51

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 19-10-2016 à 23:25:39    

xfreekingx a écrit :

Bien sur, si je pouvais tout simplement supprimer la dernière ligne ce serait mieux, mais bon, c'est déjà bien comme ca.

Quel compilateur? Avec MinGW on peut utiliser ftruncate() mais c'est pas dans la lib standard C.
 
Le code suivant est juste un "proof of concept" sans garantie, à tester/améliorer/adapter/...

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <strings.h>
  4. #include <unistd.h>
  5.  
  6. //copié de stdio.h (pourtant inclus) pour eviter un warning undefined function, pas compris...
  7. #define fileno(__F) ((__F)->_file)
  8.  
  9. #define MAX_LEN 128 //A ADAPTER
  10.  
  11. int main(void)
  12. {
  13.    FILE * fichier = fopen("test.txt", "r+b" );
  14.  
  15.    if(!fichier)
  16.    {
  17.        puts("err fopen" );
  18.        return 1;
  19.    }
  20.  
  21.    char buff[MAX_LEN + 1];
  22.    
  23.    fseek(fichier, 0, SEEK_END);
  24.    
  25.    int taille=ftell(fichier);
  26.    
  27.    printf("taille fichier: %d\n", taille);
  28.    
  29.    fseek(fichier, -MAX_LEN, SEEK_END);
  30.    
  31.    fread(buff,MAX_LEN,1,fichier);
  32.    
  33.    buff[MAX_LEN] = '\0';
  34.    
  35.    char *last_newline = strrchr(buff, '\n');
  36.    if(!last_newline)
  37.    {
  38.        puts("err strrchr, derniere ligne trop longue?" );
  39.        return 1;
  40.    }
  41.    
  42.    char *last_line = last_newline+1;
  43.    
  44.    char derniereLigne[MAX_LEN] = "";
  45.    
  46.    strncpy(derniereLigne, last_line, MAX_LEN);
  47.  
  48.    printf("ligne: \"%s\"\n", derniereLigne);
  49.  
  50.    printf("longeur ligne: %d\n", strlen(derniereLigne));
  51.  
  52.    fflush(fichier);
  53.    
  54.    if(ftruncate(fileno(fichier), taille - strlen(derniereLigne)-2))
  55.    {
  56.        puts("err ftruncate" );
  57.        return 1;
  58.    }
  59.  
  60.    fclose(fichier);
  61.  
  62.    return 0;
  63. }


(Oui j'aime bien le code aéré. :o )
 
Mais euh... Ce genre de truc sur un pdf???

Citation :

Code :
  1. fichier = fopen("AEF-2015.pdf","rb+" );


Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed