Existe-t-il une fonction qui cherche dans un flux ?

Existe-t-il une fonction qui cherche dans un flux ? - C - Programmation

Marsh Posté le 06-02-2006 à 13:28:42    

Existe-t-il une fonction (que j'ai appelée fchercher dans l'exemple ci-dessous) qui lit un flux jusqu'à tomber sur une chaîne particulière :

int truc, chose, bidule ;
 
FILE *f = popen("commande", "r" ) ;
 
fchercher(f, " truc=" ) ;
fscanf(f, "%i", &truc) ;
 
fchercher(f, " chose=" ) ;
fscanf(f, "%i", &chose) ;
 
fchercher(f, " bidule=" ) ;
fscanf(f, "%i", &bidule) ;
 
pclose(f) ;


 
Pour l'instant, je copie tout dans un buffer, ce qui me permet de chercher les champs avec strstr. Sachant que les noms des champs ne sont pas aussi évidents que dans l'exemple ci-dessus, et qu'il y a pas mal de champs, mon code source est donc difficile à comprendre pour le premier venu. En plus, la majeure partie du texte qui sort de "commande" ne m'intéresse pas... j'ai donc tout intérêt à utiliser une fonction semblable à fchercher, ce qui m'évitera d'allouer un gros buffer pour si peu.
 
Que me conseillez-vous ?

Reply

Marsh Posté le 06-02-2006 à 13:28:42   

Reply

Marsh Posté le 06-02-2006 à 13:33:57    

j'ai pas bien compris :/
strcmp ne t'aiderai pas ?

Reply

Marsh Posté le 06-02-2006 à 13:49:04    

Je te conseille de placer la liste de tes champs dans un tableau de caractères (dans le fichier en-tête, par exemple, ou dans la fonction même, à toi de voir), et de stocker les résultats dans un tableau dynamiquement alloué.
 
Comme ça, au lieu de répéter n fois la même séquence de commandes, tu écris une boucle.
 
Genre :

const char *l_field[] = { "truc", "chose", "bidule", NULL };
unsigned i = 0;
const char *field = l_field[i];
 
while ( field ) {
   ...
   field = l_field[++i];
}


Message édité par Elmoricq le 06-02-2006 à 13:50:08
Reply

Marsh Posté le 06-02-2006 à 14:52:29    

e-miel a écrit :

Existe-t-il une fonction (que j'ai appelée fchercher dans l'exemple ci-dessous) qui lit un flux jusqu'à tomber sur une chaîne particulière :

int truc, chose, bidule ;
 
FILE *f = popen("commande", "r" ) ;
 
fchercher(f, " truc=" ) ;
fscanf(f, "%i", &truc) ;
 
fchercher(f, " chose=" ) ;
fscanf(f, "%i", &chose) ;
 
fchercher(f, " bidule=" ) ;
fscanf(f, "%i", &bidule) ;
 
pclose(f) ;


 
Pour l'instant, je copie tout dans un buffer, ce qui me permet de chercher les champs avec strstr. Sachant que les noms des champs ne sont pas aussi évidents que dans l'exemple ci-dessus, et qu'il y a pas mal de champs, mon code source est donc difficile à comprendre pour le premier venu. En plus, la majeure partie du texte qui sort de "commande" ne m'intéresse pas... j'ai donc tout intérêt à utiliser une fonction semblable à fchercher, ce qui m'évitera d'allouer un gros buffer pour si peu.
 
Que me conseillez-vous ?


 
Ben le problème, c'est dans ton flux. Comment peux tu dire où s'arrête "bidule"
Exemple: Le flux contient "azertybidule=totowxcvbntruc=azertyxcvbvb"
Tu cherches "bidule=" et tu trouves "toto.....". Où t'arrêter ???
 
Faut d'abord que tu connaisses la structure de ton flux et ensuite tu peux te construire ta fonction "fchercher". Mais une telle fonction n'existe pas en natif puisqu'on ne peut pas prévoir à l'avance la tête de ton flux...

Reply

Marsh Posté le 06-02-2006 à 19:08:22    

Sve@r a écrit :

Comment peux tu dire où s'arrête "bidule"

Facile : j'attends des entiers, et scanf sait très bien s'arrêter là où il faut, du moment qu'on lui montre exactement là où il doit commencer. Le problème, c'est pas de trouver la fin de "bidule", mais c'est de trouver le début de "bidule". Tout ce que je sais, c'est qu'avant la valeur entière de bidule, il y a écrit " bidule=" (c'est-à-dire : espace, b, i, d, u, l, e, =).
 
Exemple : si ce qui sort de "commande" ressemble à ceci :

azetycuqsb dfg dfjg dhf
ret
r
er
qsd voiture=45 gfhz rgfhzej
kdghdf hello=45 g bidule=12 hrjkel h difg
of velo=78 udios

j'aimerai obtenir la valeur entière 12. J'arrive à obtenir ce 12 avec le code suivant :

FILE *f = popen("commande", "r" ) ;
char buffer[1000000] ;
fread(buffer, 1, 1000000, f) ;
pclose(f) ;
char *p = strstr(buffer, " chose=" ) ;
p += sizeof " chose=" -1 ;
int bidule ;
sscanf(p, "%i", &bidule) ;


 
Ca marche, mais avouez que ce n'est pas hyper lisible, et que ça prend beaucoup de mémoire pour le buffer. J'aimerai faire plus propre. C'est pour ça, je pense que serait mieux si on pouvait faire ça avec un flux. Mieux compris ?

Reply

Marsh Posté le 06-02-2006 à 21:00:55    

ouai flux et chaine c'est deux conceptes différents, la fonction devrait plutot s'appeler fignore

Reply

Marsh Posté le 07-02-2006 à 13:10:46    

skelter a écrit :

la fonction devrait plutot s'appeler fignore

Une telle fonction existe-t-elle ?

Reply

Marsh Posté le 07-02-2006 à 13:20:19    

Dans la libC, il n'y a pas grand chose, au fur et à mesure on se constitue souvent une bibliothèque personnelle, d'ailleurs, pour éviter de devoir ré-écrire plusieurs fois les mêmes fonctions.


Message édité par Elmoricq le 07-02-2006 à 13:20:29
Reply

Marsh Posté le 07-02-2006 à 14:50:13    

e-miel a écrit :

Facile : j'attends des entiers, et scanf sait très bien s'arrêter là où il faut


Evidemment. Dans ton exemple, tu cherches un nombre et scanf s'arrête tout seul dès qu'il trouve un caractère non numérique (un espace dans ton cas). On en revient au même point. Il faut quand-même que ton flux ait une structure bien définie. Il se trouve que c'est apparemment le cas. En l'ocurrence, je dirais que la structure de ton flux ressemble à "token=nb token=nb token=nb".
 

e-miel a écrit :

C'est pour ça, je pense que serait mieux si on pouvait faire ça avec un flux.


Le gros problème, c'est que si tu lis ton flux par bloc de "n" octets, tu risques d'avoir la fin d'un bloc sur une partie du token.
Exemple: flux=machin=12 bidule=18
Si tu lis ton flux 15 octets par 15 octets, tu auras successivement dans ton buffer

  • <machin=12 bidul>
  • <e=18>

Et tu ne trouveras jamais "bidule=". Le seul algo que je vois serait de lire ton flux octet par octet jusqu'à trouver le premier caractère d'un token. Puis voir si à partir de là on a le token en intégral. Si c'est le cas, tu renvoies ta position dans le fichier. Et si ta fonction doit être indépendante, il faut qu'elle fasse aussi l'ouverture+fermeture du flux. Ca risque d'être lourd...
 

e-miel a écrit :

Mieux compris ?


Ben j'espère que c'est plutôt toi qui a compris que scanf ne fonctionne que parce que ton flux a une structure bien définie qu'il arrive à analyser...


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

Marsh Posté le 07-02-2006 à 21:49:42    

Sve@r a écrit :

En l'ocurrence, je dirais que la structure de ton flux ressemble à "token=nb token=nb token=nb".

Pas vraiment, il y a des séquences qui n'ont rien de textuel (même si c'est fait avec des caractères ASCII) et les champs "tocken=nb" sont en plein milieu d'autre chose qui n'a pas la même apparence. Des fois, je dois lire des valeurs entre parenthèses, du type "tocken=(2,54)" alors qu'ailleurs, c'est simplement "tocken=12". Et comme lire un flux caractère par caractère me parait plus coûteux, et plus difficile à comprendre qu'un strstr dans le code source, je vais rester avec mon gros buffer et mon strstr.
 
Merci quand même pour vos propositions.

Message cité 1 fois
Message édité par e-miel le 07-02-2006 à 21:59:06
Reply

Marsh Posté le 07-02-2006 à 21:49:42   

Reply

Marsh Posté le 07-02-2006 à 21:54:13    

tu peux toujours implémenté ton fignore, et lire cararctere par carctere c'est pas tellement plus couteux, les flux sont bufferisés et meme si c'est un appel de fonction c'est pas comme si c'était un appel systeme.

Reply

Marsh Posté le 07-02-2006 à 23:12:11    

e-miel a écrit :

Pas vraiment, il y a des séquences qui n'ont rien de textuel (même si c'est fait avec des caractères ASCII) et les champs "tocken=nb" sont en plein milieu d'autre chose qui n'a pas la même apparence..


Je voulais dire "token=nb blablabla...token=nb blablabla...token=nb...".
 

e-miel a écrit :

Des fois, je dois lire des valeurs entre parenthèses, du type "tocken=(2,54)" alors qu'ailleurs, c'est simplement "tocken=12".


Alors là t'es encore plus mal barré si la structure varie. Je me demande d'ailleurs comment ton "sscanf" peut trouver l'info dans ce cas là...
 

e-miel a écrit :

Et comme lire un flux caractère par caractère me parait plus coûteux, et plus difficile à comprendre qu'un strstr dans le code source, je vais rester avec mon gros buffer et mon strstr.


Mais même là, t'as un souci si ton fichier dépasse les 100ko car ton buffer est limité à 100 000 octets...
 

e-miel a écrit :

Merci quand même pour vos propositions.


 
Pourquoi n'incluerais-tu pas dans ton "popen()" une commande complexe à base de "sed " et/ou "tr" et/ou "awk" pour essayer de tailler ton flux entrant sur un format plus uniforme (style supprimer les parenthèses par exemple) ?

Message cité 1 fois
Message édité par Sve@r le 07-02-2006 à 23:13:22

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

Marsh Posté le 07-02-2006 à 23:17:09    

pkoi ne pas utiliser flex ?


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 07-02-2006 à 23:22:24    

ou implementer un fignore, ca fait se fait en 3 lignes et c'est la question de départ

Reply

Marsh Posté le 07-02-2006 à 23:35:22    

skelter a écrit :

ou implementer un fignore, ca fait se fait en 3 lignes et c'est la question de départ


Euh... je reste un peu pantois sur l'implémentation en 3 lignes d'une fonction qui traite un tel genre de flux...


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

Marsh Posté le 07-02-2006 à 23:42:21    

tu boucles sur le resultat de fgetc et tu te sers d'un curseur, c'est franchement pas compliqué ??

Reply

Marsh Posté le 08-02-2006 à 00:12:42    

Code :
  1. int fchercher(FILE *f, const char* str)
  2. {
  3.  int i, count, ret;
  4.  for (count=i=0; str[i]; count++) {
  5.    ret = fgetc(f);
  6.    if (ret == str[i]) i++;
  7.    else if (ret == EOF) return EOF;
  8.  }
  9.  return count;
  10. }


En fait ouais st'assez court à coder (bon j'ai pas fait du propre non plus, quoique déja on à un minimum de retour d'erreur)
 
PS: oui on peut faire plus court c'est vrai :p


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 08-02-2006 à 00:28:22    

tu devrais faire le test (ret == EOF) avant, ret pourrait valoir EOF mais quand meme satisfaire le test (ret == str[i]) non ?
aussi c'est incomplet, faut remettre i à 0 quand le test (ret == str[i]) n'est plus satisfait et retester sur str[0]
 
j'ai fais ca avec en plus un nombre maximun de caracteres à ignorer (ou 0) et retourne le nombre de caracteres lus
 

Code :
  1. long fignore(FILE * stream, const char * delim, long max)
  2. {
  3. long count = 0;
  4. size_t i = 0;
  5. int c;
  6. while( (!max || count < max) && delim[i] && (c = fgetc(stream)) != EOF )
  7. {
  8.  if( c == delim[i] )
  9.   i++;
  10.  else if( c == delim[0] )
  11.   i = 1;
  12.  else
  13.   i = 0;
  14.  count++;
  15. }
  16. return count;
  17. }

Reply

Marsh Posté le 08-02-2006 à 00:43:53    

skelter a écrit :

tu devrais faire le test (ret == EOF) avant, ret pourrait valoir EOF mais quand meme satisfaire le test (ret == str[i]) non ?


Nan, si c'etait le cas la valeur de retour d'un fgetc serait complètement inutilisable. (sous entendu un string ne peut pas contenir la chaine EOF, ou alors le gars a fait exprès, et c'est tant pis pour sa gueule si le programme pète, un peu comme envoyer un string qui termine pas ... )

skelter a écrit :


aussi c'est incomplet, faut remettre i à 0 quand le test (ret == str[i]) n'est plus satisfait et retester sur str[0]


Oui effectivement, on va dire que c'est la fatigue du soir ...


Message édité par 0x90 le 08-02-2006 à 00:44:28

---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 08-02-2006 à 00:59:48    

Au passage, mon algo est faux mais le tient aussi, teste le avec :
- un fichier contenant "blahdadadadi"
- l'appel fignore(f,"dadadi",0)
Pour une solution efficace de recherche de chaine dans une chaine sans retour arrière faut utiliser le Knuth-Morris-Pratt si ma mémoire est bonne, et c'est nettement moins trivial [:0x90]


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 08-02-2006 à 09:28:17    

bien vu, j'avais pas du tout penser à ca

Reply

Marsh Posté le 08-02-2006 à 20:48:14    

en fait ca se fait assez simplement (c'est meme encore plus simple) avec memmove
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. long fignore(FILE * stream, const char * delim, long max)
  5. {
  6. long count = 0;
  7. int c;
  8. size_t len;
  9. char * s;
  10. len = strlen(delim);
  11. if( ! (s = malloc(len + 1)) )
  12.  return 0;
  13. while( (!max || count++ < max) && (c = fgetc(stream)) != EOF )
  14. {
  15.  memmove(s, s + 1, len);
  16.  s[len - 1] = c;
  17.  s[len] = '\0';
  18.  if( ! strcmp(delim, s) )
  19.   break;
  20. }
  21. free(s);
  22. return count;
  23. }


 
je sais pas si c'est efficace mais c'est simple et ca marche

Reply

Marsh Posté le 08-02-2006 à 22:38:58    

Sve@r a écrit :

Alors là t'es encore plus mal barré si la structure varie. Je me demande d'ailleurs comment ton "sscanf" peut trouver l'info dans ce cas là...

Si la chaîne recherchée est "bidule=(" puis "," il n'y a aucun problème. Faut bricoler, mais ça marche.

Sve@r a écrit :

Mais même là, t'as un souci si ton fichier dépasse les 100ko car ton buffer est limité à 100 000 octets...

J'aurai plutôt dit une limite à 8 Mo (taille de la pile)... en supposant que les fonctions déjà empilées (fonctions appelantes) ne prennent pas beaucoup de place dans la pile (par chance, c'est le cas).

Sve@r a écrit :

Pourquoi n'incluerais-tu pas dans ton "popen()" une commande complexe à base de "sed " et/ou "tr" et/ou "awk" pour essayer de tailler ton flux entrant sur un format plus uniforme (style supprimer les parenthèses par exemple) ?

Les noms et l'aspect des champs n'est pas si régulier que ça, je me retrouverai avec un sed compliqué. Sinon, le awk c'est comme du C interprété, donc aurant rester au C.
 
Sinon, pour toutes vos méthodes, je ne dis pas qu'elles ne marchent pas, mais ce que je cherche c'est le code source le plus simple possible... donc, on va en rester là. Merci quand même. ;)


Message édité par e-miel le 08-02-2006 à 22:41:18
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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