[RESOLU] ungetc: propre ou pas...

ungetc: propre ou pas... [RESOLU] - C - Programmation

Marsh Posté le 04-10-2006 à 21:16:05    

Bonjour à tous,
J'ai un problème à résoudre et j'ai une solution à base de "ungetc()", et une sans. Il n'y a pas de miracle, pour ma solution sans, je mémorise le caractère en question et j'indique par un flag de ne pas lire le fichier au tour de boucle suivant donc au résultat, c'est pareil.
Mon pb, c'est que dans le man de "ungetc()", il est indiqué que la fonction ne garantit le résultat que pour un seul caractère. Et là, cette simple phrase me pose une foule de questions
1) pourquoi un seul caractère
2) est-il judicieux d'utiliser cette fonction
En effet, il existe dans la lib standard une foule de fonctions assez dangereuses (strtok ou d'autres dont je ne me souviens pas) et je me demande si ungetc() n'en fait pas partie...


Message édité par Sve@r le 06-10-2006 à 21:54:34

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 04-10-2006 à 21:16:05   

Reply

Marsh Posté le 04-10-2006 à 21:23:41    

et c'est quoi le problème ?

Reply

Marsh Posté le 04-10-2006 à 21:30:37    

1) parce que c'est ce que le standard dit :)
2) dans certain cas, ça se justifie. mais à cause de 1) c'est souvent peu pratique à utiliser.

Reply

Marsh Posté le 04-10-2006 à 21:38:58    

mais si ton utilisation est cadrée et plus concise plus élégante que l'autre, ne t'en prive pas.

Reply

Marsh Posté le 04-10-2006 à 21:43:58    

bjone a écrit :

et c'est quoi le problème ?


Ben le problème était

Citation :

est-ce "propre" d'utiliser "ungetc()"


Taz a écrit :

mais si ton utilisation est cadrée et plus concise plus élégante que l'autre, ne t'en prive pas.


Tout à fait. Plus cadrée, plus concise et plus élégante. Je la préfère à celle où je me bats avec un flag oui/non que j'ai créé juste pour ça...
Merci  :sol:


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 04-10-2006 à 21:45:37    

non c'est pas ça le problème.
 

Citation :

J'ai un problème à résoudre et j'ai une solution à base de "ungetc()", et une sans.


 
il aurait été utile de savoir quel était le problème à résoudre pour savoir si il existait une solution plus propre ou pas.

Reply

Marsh Posté le 04-10-2006 à 21:47:34    

Sve@r a écrit :

Ben le problème était  
 
Tout à fait. Plus cadrée, plus concise et plus élégante. Je la préfère à celle où je me bats avec un flag oui/non que j'ai créé juste pour ça...
Merci  :sol:


 
un flag peut être plus élégant et plus économique que de repousser des données dans un flux qui va ensuite être relu  dans une autre fonction  [:spamafote]

Reply

Marsh Posté le 05-10-2006 à 09:33:49    

bjone a écrit :

un flag peut être plus élégant et plus économique que de repousser des données dans un flux qui va ensuite être relu  dans une autre fonction  [:spamafote]


Mouais... déjà c'est une donnée et non des données... et si

  • la fonction est sans effet secondaire néfaste
  • conseillée par Taz

Alors je m'arrête là. Je ne vais pas me compliquer la vie alors que j'ai une solution simple à mettre en oeuvre qui répond à mon besoin...
 

bjone a écrit :

il aurait été utile de savoir quel était le problème à résoudre pour savoir si il existait une solution plus propre ou pas.


Si c'est juste informatif, alors je peux en effet exposer le problème et donner ma solution...
Le problème (simplifié) est une espèce de "grep" sur plusieurs chaînes à la fois. Donc j'ai un fichier qui contient "azertyuiop" et je cherche "ert" et/ou "uio".
Solution: Je lis un carac. et je regarde si ce carac. comparé au premier carac de mes chaînes y est. Il peut y être 0 ou "n" fois.
S'il n'y est pas, c'est réglé, je passe au carac. suivant.
S'il y est plusieurs fois, je rajoute un 2° caractère lu et je recompare ces 2 avec chaque "2 premiers carac." de mes chaînes. Et je continue à rajouter des carac. lus tant que la comparaison me donne plusieurs possibilités jusqu'à ce que finallement je tombe sur la bonne.
Si en cours de route je ne trouve plus de correspondance (par exemple je cherche "erto", l'algo en lisant "azertyuiop" trouvera "e", puis "er", puis "ert" mais ne trouvera pas "erty" ) alors je passe ces 4 lettres "erty" et je recommence tout l'algo avec "y".
En revanche, et c'est là que "ungetc()" entre en jeu, je peux avoir un faux début de chaîne immédiatement suivi de la vraie chaîne. Par exemple, le fichier contient azertertyuiop" et je cherche "erty".
L'algo cherche "e" => ok
Il cherche ensuite "er" => ok
Il cherche ensuite "ert" => ok
Il cherche ensuite "erte" => Pas bon mais ce second "e" est le vrai début de la chaîne "erty". Je ne peux pas me permettre de le rater sinon l'algo recommencerait à zéro avec une chaîne commençant par "r" qu'il ne trouverait pas.
Solution: Je replace ce dernier "e" dans le fichier et, au tour de boucle suivant, l'algo le relit et recommence à zéro avec "e" puis "er" puis "ert" puis "erty" et trouve ma chaîne.
 
J'espère avoir été clair...

Message cité 1 fois
Message édité par Sve@r le 05-10-2006 à 09:58:21

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 05-10-2006 à 11:00:02    

pourquoi n'utilise tu pas strstr... (sof si tu veux tout faire)
:p

Message cité 1 fois
Message édité par big_dadi_fat le 05-10-2006 à 11:01:00
Reply

Marsh Posté le 05-10-2006 à 13:58:37    

big_dadi_fat a écrit :

pourquoi n'utilise tu pas strstr... (sof si tu veux tout faire)
:p


Ben parce que strstr() ne marche que si tu as déjà la chaîne entière dans laquelle chercher. Cela m'obligerait à charger tout le fichier dans une grosse chaîne et ce n'est pas viable avec un gros fichier.
Et si je voulais travailler par blocs de "n" octets, il y aurait toujours le risque que mon bloc de lecture s'arrête au milieu d'une des chaînes à trouver...


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 05-10-2006 à 13:58:37   

Reply

Marsh Posté le 05-10-2006 à 14:04:28    

Sve@r a écrit :

Solution: Je replace ce dernier "e" dans le fichier et, au tour de boucle suivant, l'algo le relit et recommence à zéro avec "e" puis "er" puis "ert" puis "erty" et trouve ma chaîne.

je comprends pas trop pourquoi tu es obligé de faire ça : plutôt que de réinitialiser ta "graine" et relire le dernier 'e', pourquoi ne pourrais-tu pas remettre la "graine" à "e" pour recommencer directement le tour de boucle suivant comme si de rien était.
 
Ton algo me semble en gros correspondre à un automate fini, donc il ne devrait pas y avoir besoin de revenir en arrière dans le flux de données...


---------------
TriScale innov
Reply

Marsh Posté le 05-10-2006 à 15:19:58    

je pense qu'il y a un problème d'approche dans l'implémentation de l'ensemble, pourquoi ne pas implémenter un buffer avec un pointeur de début de recherche et un pointeur de recherche courante...
 
enfin ça me parait pas naturel, il n'y a pas besoin de charger tout le fichier en mémoire, mais avec un buffer dynamique (et initialment un minimum large, genre 256 octets ou tout simple de la taille de la plus grande recherchée aligné sur la puissance de 2 supérieure pour reboucler trivialment quand tu alimente le buffer) il devrait y avoir un moyen de moyenner.
 
tu maintiens des couples début de recherche+indice de progression pour tes chaines à rechercher, enfin y'a des moyens plus élégants je pense. (en tout cas je sens le truc que j'aimerai pas repasser derrière)

Reply

Marsh Posté le 05-10-2006 à 16:31:33    

franceso a écrit :

je comprends pas trop pourquoi tu es obligé de faire ça : plutôt que de réinitialiser ta "graine" et relire le dernier 'e', pourquoi ne pourrais-tu pas remettre la "graine" à "e" pour recommencer directement le tour de boucle suivant comme si de rien était.


C'est la solution sans le "ungetc()" dont je parle au début du topic. Je replace le "e" au début du buffer. Mais cela impose un flag oui/non pour ne pas lire le fichier au tour de boucle suivant. Et c'est là que je demandais si "ungetc()" qui est quand-même plus pratique était propre ou pas...
 

bjone a écrit :

je pense qu'il y a un problème d'approche dans l'implémentation de l'ensemble, pourquoi ne pas implémenter un buffer avec un pointeur de début de recherche et un pointeur de recherche courante...


Je suis allé au plus simple...
 

bjone a écrit :

enfin ça me parait pas naturel, il n'y a pas besoin de charger tout le fichier en mémoire, mais avec un buffer dynamique (et initialment un minimum large, genre 256 octets ou tout simple de la taille de la plus grande recherchée aligné sur la puissance de 2 supérieure pour reboucler trivialment quand tu alimente le buffer) il devrait y avoir un moyen de moyenner.


Donnes une solution. Mais tu vas te casser les dents si tu as un fichier contenant "azertyuiop" que tu lis de 5 en 5 et que tu cherches "rty". Tu auras "azert" qui ne contient pas "rty" puis "yuiop" qui ne contient toujours pas "rty"...
 

bjone a écrit :

(en tout cas je sens le truc que j'aimerai pas repasser derrière)


Tu deviens insultant. J'ai une fonction à laquelle je passe mon buffer et mon tableau de chaînes et elle me renvoie le nb de chaînes correspondantes au buffer. Et l'algo principal est de ce type

TQ infini
   lire fichier et charger le caractère lu dans un pointeur de mon buffer
   si EOF fin boucle
 
   mettre le caractère suivant le pointeur à 0 pour que mon buffer devienne une chaîne
   evaluation nb fois ou buffer est vu dans tableau des chaînes
         0: // Pas trouvé
             si buffer contient plus d'un caractère
                 remettre dernier carac dans fichier => ungetc()
             fin si
             Réinitialisation buffer => La lecture suivante recommencera tout à 0
             break
 
         1: // Trouvé chaîne
             Traitement de ce cas qui m'intéresse
             Réinitialisation buffer
             break
 
         autre: // Il y a plusieurs possibilités
             incrément pointeur pour que le caractère suivant aille à la suite du buffer
     fin éval
fin tq


 
Me dis pas que c'est compliqué...

Message cité 2 fois
Message édité par Sve@r le 05-10-2006 à 16:49:33

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 05-10-2006 à 16:38:46    

On pourrait néanmoins envisager une solution à base de :

debut_buffer = adresse du buffer
TANT QUE lecture des (taille buffer - debut_buffer) caracteres vers debut_buffer  
   TANT QUE recherche de la chaine dans le buffer est fructueuse
      Effectuer les operations qui-vont-bien avec les resultats de la recherche
   FIN TANT QUE
 
   SI ( fin derniere chaine trouvee != fin du buffer  
         ET n derniers caracteres == n premiers de la recherche )
      copier les n derniers caracteres du buffer au debut
      debut_buffer = adresse du buffer + n
   SINON
      debut_buffer = adresse du buffer
   FIN SI      
FIN TANT QUE


 
A priori c'est plus simple à mettre en place. Enfin à mon sens.

Message cité 1 fois
Message édité par Elmoricq le 05-10-2006 à 16:51:05
Reply

Marsh Posté le 05-10-2006 à 16:54:18    

Elmoricq a écrit :

On pourrait néanmoins envisager une solution à base de :

debut_buffer = adresse du buffer
TANT QUE lecture des (taille buffer - debut_buffer) caracteres vers debut_buffer  
   TANT QUE recherche de la chaine dans le buffer est fructueuse
      Effectuer les operations qui-vont-bien avec les resultats de la recherche
   FIN TANT QUE
 
   SI ( fin derniere chaine trouvee != fin du buffer  
         ET n derniers caracteres == n premiers de la recherche )
      copier les n derniers caracteres du buffer au debut
      debut_buffer = adresse du buffer + n
   SINON
      debut_buffer = adresse du buffer
   FIN SI      
FIN TANT QUE


 
A priori c'est plus simple à mettre en place. Enfin à mon sens.


 
Je vois l'idée. Tu replaces les caractères qui ont fait que la recherche a échoué au début du buffer pour qu'ils soient pris en compte à la recherche suivante. C'est encore un peu flou mais je vais y réfléchir. A priori, je vois un problème si le buffer entier correspond à une ou plusieurs chaînes cherchées mais je dis ça d'instinct...


Message édité par Sve@r le 05-10-2006 à 16:58:08

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 05-10-2006 à 17:02:30    

En fait, il suffit de faire un "strrchr(buffer, premier caractère de la recherche)", et vérifier qu'on n'obtient ni NULL, ni une adresse telle que "(fin buffer - adresse) > longueur de la recherche". Sinon n vaut 0.
 
Ensuite, soit tu ne t'embêtes pas et tu recopies à partir de là, avec n ==  (fin buffer - adresse), sans rien vérifier de plus.  
Soit tu exécutes un strncmp() avec n comme paramètre, ce qui t'épargnerais des recopies inutiles.
 
Et voila, tu peux à nouveau remplir ton buffer et recommencer la recherche, comme si de rien n'était.
 
edit : et si tu as plusieurs chaînes à rechercher, tu simplifies l'algorithme en considérant la plus grande valeur de n ainsi obtenue


Message édité par Elmoricq le 05-10-2006 à 17:09:16
Reply

Marsh Posté le 05-10-2006 à 18:40:41    

Sve@r a écrit :

C'est la solution sans le "ungetc()" dont je parle au début du topic. Je replace le "e" au début du buffer. Mais cela impose un flag oui/non pour ne pas lire le fichier au tour de boucle suivant.

Désolé, mais je comprends toujours pas pourquoi tu as besoin de ce flag: si tu te rends compte que la chaine cherchée n'apparaît pas dans ton buffer, tu veux recommencer avec seulement le dernier caractère lu. Mais tu sais déjà que le prochain tour de boucle va tomber dans le cas 0 (à moins que tu recherches une chaîne de 1 caractère, ce qui est un cas qu'on peut trivialement éviter). Donc à mon avis, tu peux skipper le tour de boucle inutile en réinitialisant directement ton buffer avec le dernier caractère, et en lisant le caractère suivant au tour de boucle suivant.
 
De manière générale, j'ai un gros doute sur ton algorithme : tu as tous ces problèmes pour traiter correctement les cas où la première lettre du mot cherché apparaît plusieurs fois dedans. Mais que fais tu des cas où c'est tout un préfixe qui  se répète.
 
Exemple: si tu recherches "az1az2" dans "az1az1az2"
1- tu commences par matcher "az1az" dans ton buffer
2- "az1az1" ne matche pas
    => tu reprends du début en réinitialisant ton buffer à "1"
3- "1az2" ne matche pas
    => tu n'as pas vu qu'il y avait une occurence du motif
 
Pour des cas comme ça, tu es obligé de garder dans ton buffer le plus grand préfixe de la chaine à chercher, et non pas simplement le dernier caractère lu.
 
AMHA, la meilleure solution consiste à programmer un automate fini qui reconnaît les motifs que tu cherches. Tu devrais sans doute trouver plein de docs à ce sujet.


---------------
TriScale innov
Reply

Marsh Posté le 05-10-2006 à 21:28:21    

franceso a écrit :

Exemple: si tu recherches "az1az2" dans "az1az1az2"
1- tu commences par matcher "az1az" dans ton buffer
2- "az1az1" ne matche pas
    => tu reprends du début en réinitialisant ton buffer à "1"
3- "1az2" ne matche pas
    => tu n'as pas vu qu'il y avait une occurence du motif


Tiens... c'est vrai !!!
Hum... :(  ok - Je vais revoir tout ça...
 


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 06-10-2006 à 20:21:45    

Ok - J'ai tout refait pour y inclure le cas de Francesco (auquel je n'avais pas pensé)
1) je lis un bloc
2) j'y recherche mes chaînes avec "strstr()" (merci à big_dadi_fat)
3) une fois le bloc traité, je regarde la plus grosse chaîne placée en fin de bloc qui pourrait correspondre à une des chaînes cherchées et je déplace les "n" derniers caractères correspondants au début du bloc puis je continue à remplir la suite du bloc avec la suite du fichier (merci à Elmoricq)
 
Seul petit bémol: Si un autre programmeur reprend le code et qu'il retaille le bloc de lecture à une taille infèrieure à la plus grosse des chaînes, cela plantera tout l'algo. Ca pourrait s'arranger avec un joli malloc mais c'est dommage de faire un malloc juste pour ça donc un commentaire intelligent devrait suffire  :D  
 
Et en plus, je sais que "ungetc()" est propre au cas où j'en aurais besoin un jour (merci à Taz)

Message cité 1 fois
Message édité par Sve@r le 06-10-2006 à 21:29:04

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 07-10-2006 à 13:00:20    

Sve@r a écrit :

Seul petit bémol: Si un autre programmeur reprend le code et qu'il retaille le bloc de lecture à une taille infèrieure à la plus grosse des chaînes, cela plantera tout l'algo. Ca pourrait s'arranger avec un joli malloc mais c'est dommage de faire un malloc juste pour ça donc un commentaire intelligent devrait suffire  :D


 
Ben un petit malloc() en début de fonction me semble quand même préférable, surtout que ça n'alourdit pas le code, avec un petit calcul de la taille avant pour définir la taille du buffer à au moins j'sais-pas-combien x longueur de la chaîne la plus longue. Comme ça tu es tranquille.

Reply

Marsh Posté le 07-10-2006 à 20:26:49    

Sve@r a écrit :

Donnes une solution. Mais tu vas te casser les dents si tu as un fichier contenant "azertyuiop" que tu lis de 5 en 5 et que tu cherches "rty". Tu auras "azert" qui ne contient pas "rty" puis "yuiop" qui ne contient toujours pas "rty"...


 
tu maintiens un buffer circulaire de la taille de la plus grande chaine à rechercher, avec un curseur de remplissage et un curseur d'origine, et tu maintiens 'n' structures stockant chaque chaine a rechercher ainsi le début de l'occurance courante, un flag pour savoir si t'as trouvé quelque chose ou pas.
Et si le curseur d'origine courante n'est pas le début d'une des 'n' chaines à trouver tu l'avances ouvrant alors un espace à remplir depuis le fichier balayé.

Message cité 1 fois
Message édité par bjone le 07-10-2006 à 20:27:29
Reply

Marsh Posté le 07-10-2006 à 21:45:56    

bjone a écrit :

tu maintiens un buffer circulaire de la taille de la plus grande chaine à rechercher, avec un curseur de remplissage et un curseur d'origine, et tu maintiens 'n' structures stockant chaque chaine a rechercher ainsi le début de l'occurance courante, un flag pour savoir si t'as trouvé quelque chose ou pas.
Et si le curseur d'origine courante n'est pas le début d'une des 'n' chaines à trouver tu l'avances ouvrant alors un espace à remplir depuis le fichier balayé.


 
Elégante solution... :sol:
 
 
 


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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