parser une s

Le
xylo
bonjour à tous,

existe-t-il un algo élégant pour parser une séquence du type:
x2x50x30x3x10
et placer le contenu dans un tableau de byte ?

en C ou en C++


--
Apply rot13 to this e-mail address before using it.
JM Marino
http://jm.marino.free.fr
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Anthony Gelibert
Le #17615211
On 2008-10-22 19:20:08 +0200, xylo
bonjour à tous,

existe-t-il un algo élégant pour parser une séquence du type:
x2x50x30x3x10
et placer le contenu dans un tableau de byte ?

en C ou en C++


Bonjour,
l'utilisation d'un automate d'état reconnaissant le vocabulaire : x##
(# est un chiffre ou rien) pourrait faire l'affaire je pense. De plus
cela aurait une certaine élégance...
Maintenant si par élégance tu entends compacité, une petite boucle fera
l'affaire...
Antoine Leca
Le #17622531
En news:48ff60c8$0$15722$, xylo va escriure:
existe-t-il un algo élégant pour parser une séquence du type:
x2x50x30x3x10
et placer le contenu dans un tableau de byte ?



Il manque la définition de l'élégance.

Pour certains (qui parraissent nombreux à lire le code industriellement
utilisé), une réponse « élégante » serait de rajouter «char tableau[]="»
avant, un «";» après, envoyer le tout au compilo C et récupérer le résultat.
Une autre forme de la même réponse est d'embarquer dans le programme un
interpréteur C/AWK/Python/xxx dans le programme, de lui passer la séquence
et de récupérer le résultat.


Pour d'autres, l'« élégance » serait une boucle avec un
sscanf(,"\x%[...]%n",);
Le problème d'élégance devient alors de gérer « élégamment » les
contingences d'espace ; utiliser un tableau de taille fixé et for(;;) n'est
évidemment pas élégant, trop goret, ce ne serait pas homogène avec le reste.
Un tableau dynamique et gérer un realloc ou similaire lorsque la taille de
la séquence croît non plus (ÀMHA), cela pue trop la sueur. Je proposerais
donc plutôt un algorithme à base de tuyau/producteur-consommateur/coroutine
en aval du sscanf.


Antoine
xylo
Le #17624981
Le Thu, 23 Oct 2008 16:23:29 +0200, Antoine Leca a écrit:

En news:48ff60c8$0$15722$, xylo va escriure:
existe-t-il un algo élégant pour parser une séquence du type:
x2x50x30x3x10
et placer le contenu dans un tableau de byte ?



Il manque la définition de l'élégance.

Pour certains (qui parraissent nombreux à lire le code industriellement
utilisé), une réponse « élégante » serait de rajouter «char tableau[]="»
avant, un «";» après, envoyer le tout au compilo C et récupérer le résultat.
Une autre forme de la même réponse est d'embarquer dans le programme un
interpréteur C/AWK/Python/xxx dans le programme, de lui passer la séquence
et de récupérer le résultat.


Pour d'autres, l'« élégance » serait une boucle avec un
sscanf(,"\x%[...]%n",);
Le problème d'élégance devient alors de gérer « élégamment » les
contingences d'espace ; utiliser un tableau de taille fixé et for(;;) n'est
évidemment pas élégant, trop goret, ce ne serait pas homogène avec le reste.
Un tableau dynamique et gérer un realloc ou similaire lorsque la taille de
la séquence croît non plus (ÀMHA), cela pue trop la sueur. Je proposerais
donc plutôt un algorithme à base de tuyau/producteur-consommateur/coroutine
en aval du sscanf.


Antoine



donc si je comprends bien il faut que je réinvente le fil à couper le
beurre ?!
je pensais trop naïvement que le sujet avait déjà été traité...

bon ben je vais m'y coller en C++ avec des vecteurs et des std::string...

--
Apply rot13 to this e-mail address before using it.
JM Marino
http://jm.marino.free.fr
Eric Levenez
Le #17625661
Le 23/10/08 19:50, dans
donc si je comprends bien il faut que je réinvente le fil à couper le
beurre ?!
je pensais trop naïvement que le sujet avait déjà été traité...



Faire une boucle pour lire quelques octets, c'est le b.a-ba de la
programmation. Cela doit se faire en 10 lignes maximum et en 2 minutes. De
là à dire que c'est réinventer la roue, c'est poussez bien loin le bouchon.
Mais effectivement certains préfèrent faire à la place une recherche d'une
heure sur Internet pour faire un simple couper/coller au lieu de réfléchir.

bon ben je vais m'y coller en C++ avec des vecteurs et des std::string...



Tu utilises la marteau pilon de ton choix.

--
Éric Lévénez -- Unix is not only an OS, it's a way of life.
xtof.pernod
Le #17626191
Eric Levenez a fait rien qu'à écrire :
Le 23/10/08 19:50, dans
donc si je comprends bien il faut que je réinvente le fil à couper le
beurre ?!


(...)
bon ben je vais m'y coller en C++ avec des vecteurs et des std::string...



Tu utilises la marteau pilon de ton choix.




Antoine Leca a fait rien qu'à écrire :
> En news:48ff60c8$0$15722$, xylo va escriure:
>> (...)
> (...) Je proposerais
> donc plutôt un algorithme à base de tuyau/producteur-consommateur/coroutine
> en aval du sscanf.

Fectivement, y'a moyen de faire compliqué =)

Sinon, dans le même esprit tordu mais dans la direction inverse, j'ai en
une ligne:

nb_val = sscanf( inbuf, "\x%hhx\x%hhx\x%hhx\x%hhx",
outbuf, outbuf+1, outbuf+2, outbuf+3 );


L'élégance est certes discutable, mais à programmer, c'est garanti sans sueur..

Comme il n'y pas d'indication ni de contraintes sur l'entrée, on
supposera que c'est une fonction avec comme paramètres un buffer
d'entrée, de taille finie (donc qui implique un nbre maxi. de %hhx,
l'exemple donné est lavable jusqu'à 4) et un de sortie, suffisement
dimensionné..

--
Christophe,
xylo
Le #17634061
Le Thu, 23 Oct 2008 22:00:08 +0200, xtof.pernod a écrit:

Eric Levenez a fait rien qu'à écrire :
Le 23/10/08 19:50, dans
donc si je comprends bien il faut que je réinvente le fil à couper le
beurre ?!


(...)
bon ben je vais m'y coller en C++ avec des vecteurs et des std::string...



Tu utilises la marteau pilon de ton choix.




Antoine Leca a fait rien qu'à écrire :
> En news:48ff60c8$0$15722$, xylo va escriure:
>> (...)
> (...) Je proposerais
> donc plutôt un algorithme à base de tuyau/producteur-consommateur/coroutine
> en aval du sscanf.

Fectivement, y'a moyen de faire compliqué =)

Sinon, dans le même esprit tordu mais dans la direction inverse, j'ai en
une ligne:

nb_val = sscanf( inbuf, "\x%hhx\x%hhx\x%hhx\x%hhx",
outbuf, outbuf+1, outbuf+2, outbuf+3 );


L'élégance est certes discutable, mais à programmer, c'est garanti sans sueur..

Comme il n'y pas d'indication ni de contraintes sur l'entrée, on
supposera que c'est une fonction avec comme paramètres un buffer
d'entrée, de taille finie (donc qui implique un nbre maxi. de %hhx,
l'exemple donné est lavable jusqu'à 4) et un de sortie, suffisement
dimensionné..




whoa super le sscanf (non je te chambre, je connais bien sure) mais mon
problème et qd même un peu plus complexe...

méa culpa je n'avais pas préciser que :
1) la séquence des xXX n'est pas limitée,
2) la forme xX ou x0X peut être utilisée

bon mais merci qd même pour vos réponses qui ne m'ont pas été d'un grand
secours. mais c'est le geste qui compte.

pour répondre à EL, j'ai tout trouvé sur Internet et en moins d'une heure.
bilan : ça marche pile poil et j'ai pas réinventé le fils à couper le
beurre. l'objectif est atteind et je n'ai aucunement mauvaise conscience...

le C/C++ bien écrit doit pouvoir être réutilisé alors pourquoi faire la
fine bouche et s'en priver...

à plus et je ne vous dis pas merci.

--
Apply rot13 to this e-mail address before using it.
JM Marino
http://jm.marino.free.fr
xylo
Le #17688041
Le Wed, 22 Oct 2008 17:20:08 +0000, xylo a écrit:

bonjour à tous,

existe-t-il un algo élégant pour parser une séquence du type:
x2x50x30x3x10
et placer le contenu dans un tableau de byte ?

en C ou en C++





/*----------------------------------------*/
static char *tokenizer(char *s, char *sep)
{
static char c;
static char *last=NULL;
static int len=0;

if (!s) {
if (!last) {
return NULL;
}
last[len] = c;
last += len;
s = last;
}

last = s + strspn(s, sep);
if (!*last) {
last=NULL;
return NULL;
}
len = strcspn(last, sep);
c = last[len];
last[len] = '';

return last;
}

/*----------------------------------------*/
int main(int argc, char** argv) {
char test_1[255] = "\x2\x52\x20\x71";
char test_2[255] = "\x10\x2\x010\x1";
char *t = NULL;

printf("ntest_1");
for( t = tokenizer( test_1, "\x") ; t ; t = tokenizer( NULL, "\x")) {
printf("ntoken: %s", t);
}
printf("n");

printf("ntest_2");
for( t = tokenizer( test_2, "\x") ; t ; t = tokenizer( NULL, "\x")) {
printf("ntoken: %s", t);
}
printf("n");

return 0;
}

reste à convertir en hexa et à placer dans un tableau de bytes... C TOUT.

--
Apply rot13 to this e-mail address before using it.
JM Marino
http://jm.marino.free.fr
xylo
Le #17689831
Le Thu, 30 Oct 2008 09:31:48 +0100, Charlie Gordon a écrit:

"xylo" 49095805$0$4653$
Le Wed, 22 Oct 2008 17:20:08 +0000, xylo a écrit:

bonjour à tous,

existe-t-il un algo élégant pour parser une séquence du type:
x2x50x30x3x10
et placer le contenu dans un tableau de byte ?

en C ou en C++




/*----------------------------------------*/
static char *tokenizer(char *s, char *sep)



Un petit commentaire qui décrit l'API serait le bienvenu.

{
static char c;
static char *last=NULL;
static int len=0;



Horreur, malheur!
Pourquoi calquer cette fonction sur strtok avec ses defauts alors qu'ils
serait facile de passer un pointeur sur un etat ou plus simplement un
pointeur sur un pointeur.
Comme strtok cette fonction modifie la chaine s, c'est mal, même si en fin
de compte le contenu en est restauré par l'appel suivant avec NULL.
sep devrait être déclarée de type const char *



Je suis d'accord, le code est plustôt pourri, mais bon j'ai pas trouvé
mieux pour l'instant... (modification de la chaine source (si
possible), pas thread safe...)


if (!s) {
if (!last) {
return NULL;
}
last[len] = c;
last += len;
s = last;
}

last = s + strspn(s, sep);



Relire la spec de strspn et strcspn, ces fonctions ne font pas ce que tu
penses, ou alors l'algorithme est très approximatif pour le problème
posé.




Ok, je consulte le man...

if (!*last) {
last=NULL;
return NULL;
}
len = strcspn(last, sep);
c = last[len];
last[len] = '';

return last;
}

/*----------------------------------------*/ int main(int argc, char**
argv) {
char test_1[255] = "\x2\x52\x20\x71"; char test_2[255] >> "\x10\x2\x010\x1";



Notons qu'il faut absolument faire des copies locales des chaines. avec
char *test_1 = \x2\x52\x20\x71; tokenizer essaierait de modifier la
constante chaine de caractères, soit un comportement indefini qui
déclanche une exception sur beaucoup d'OS récents. D'autre part, il
n'est pas nécessaire de definir la taille de ces tableaux, il serait
préférable de laisser le compilateur les allouer au plus juste avec

char test_1[] = "\x2\x52\x20\x71"; char test_2[] > "\x10\x2\x010\x1";

char *t = NULL;

printf("ntest_1");
for( t = tokenizer( test_1, "\x") ; t ; t = tokenizer( NULL, "\x"))
{
printf("ntoken: %s", t);
}
printf("n");



J'ai comme l'impression que l'API implementée ci-dessus est utilisée à
tort et à travers ici. la chaine "\x" passée à tokenizer va être
interprétée par cette dernière comme une chaine de séparateurs
possibles, pas comme un séparateur de plusieurs caractères... on va
donc s'arrêter sur n'importe quelle séquence de caractères '\' et/ou
'x'

printf("ntest_2");
for( t = tokenizer( test_2, "\x") ; t ; t = tokenizer( NULL, "\x"))
{
printf("ntoken: %s", t);
}
printf("n");

return 0;
}

reste à convertir en hexa et à placer dans un tableau de bytes... C
TOUT.



Non, cette proposition n'est pas une bonne base pour le problème posé,
quant à l'élégance, on en est fort loin.

Voici une fonction qui parse la chaine en poussant un pointeur :

/* pp est l'adresse d'un pointeur const char * qui sera avancé à la
position suivante en cas de succès.
la fonction retourne la valeur de l'octet encodé ou -1 en fin de chaine
ou si l'encodage est incorrect (pas de x, pas d'encodage hexadecimal,
trop de chiffres...)
*/

#include #include #include
int getbyte(const char **pp) {
const char *p = *pp;

if (p[0] == '\' && p[1] == 'x' && isxdigit((unsigned char)p[2]) &&
(!isxdigit((unsigned char)p[3]) || !isxdigit((unsigned
char)p[4]))) {
return (int)strtoul(p + 2, (char **)pp, 16);
}
return -1;
}


/* Voici un exemple d'utilisation: */

void parse(const char *p) {
int c;

printf("parsing "%s"n", p);
while ((c = getbyte(&p)) >= 0) {
printf("got %dn", c);
}
if (*p) {
printf("parsing stopped at "%s"n", p);
}
}

int main() {
parse("\x2\x52\x20\x71");
parse("\x2\x0\x");
parse("\x001");
parse("\x123");
parse("\x 1");
parse("\x+1");
parse("\x+12");
parse("\x-1");
return 0;
}

Ce n'est pas particulièrement élégant à cause des idiosyncrasies de la
librairie standard.
On peut aussi utiliser sscanf, qu'en règle générale je déconseille à
cause des nombreux pièges qu'elle recelle :

int getbyte(const char **pp) {
unsigned int value;
int n;

if (sscanf(*pp, "\x%2x%n", &value, &n) == 1) {
*pp += n;
return value;
}
return -1;
}

Cette solution moins efficace accepte aussi les séquences "x 1",
"x+1", "x-1" et le diagnostique sur les séquences trop longues est
incorrect.

Pour une solution élégante, il faut changer de langage et utiliser des
expressions régulières.



Entièrement d'accord (mais j'ai pas le choix), vive Perl !!!


--
http://jm.marino.free.fr
xylo
Le #17689821
Le Thu, 30 Oct 2008 09:31:48 +0100, Charlie Gordon a écrit:

Pour une solution élégante, il faut changer de langage et utiliser des
expressions régulières.



Mais si je ne m'abuse, les regexp existent bien en C ?! c'est
peut être une piste + qu'intéressante...

http://nicolasj.developpez.com/articles/regex/

Voilà la piste que j'attendais depuis le début du post. Tant de bla-bla
pour en arriver là, c'est dommage ?!

Merci à tous pour votre aide.

--
http://jm.marino.free.fr
Charlie Gordon
Le #17688921
"xylo" 49095805$0$4653$
Le Wed, 22 Oct 2008 17:20:08 +0000, xylo a écrit:

bonjour à tous,

existe-t-il un algo élégant pour parser une séquence du type:
x2x50x30x3x10
et placer le contenu dans un tableau de byte ?

en C ou en C++




/*----------------------------------------*/
static char *tokenizer(char *s, char *sep)



Un petit commentaire qui décrit l'API serait le bienvenu.

{
static char c;
static char *last=NULL;
static int len=0;



Horreur, malheur!
Pourquoi calquer cette fonction sur strtok avec ses defauts alors qu'ils
serait facile de passer un pointeur sur un etat ou plus simplement un
pointeur sur un pointeur.
Comme strtok cette fonction modifie la chaine s, c'est mal, même si en fin
de compte le contenu en est restauré par l'appel suivant avec NULL.
sep devrait être déclarée de type const char *

if (!s) {
if (!last) {
return NULL;
}
last[len] = c;
last += len;
s = last;
}

last = s + strspn(s, sep);



Relire la spec de strspn et strcspn, ces fonctions ne font pas ce que tu
penses, ou alors l'algorithme est très approximatif pour le problème posé.

if (!*last) {
last=NULL;
return NULL;
}
len = strcspn(last, sep);
c = last[len];
last[len] = '';

return last;
}

/*----------------------------------------*/
int main(int argc, char** argv) {
char test_1[255] = "\x2\x52\x20\x71";
char test_2[255] = "\x10\x2\x010\x1";



Notons qu'il faut absolument faire des copies locales des chaines.
avec char *test_1 = \x2\x52\x20\x71; tokenizer essaierait de modifier la
constante chaine de caractères, soit un comportement indefini qui déclanche
une exception sur beaucoup d'OS récents. D'autre part, il n'est pas
nécessaire de definir la taille de ces tableaux, il serait préférable de
laisser le compilateur les allouer au plus juste avec

char test_1[] = "\x2\x52\x20\x71";
char test_2[] = "\x10\x2\x010\x1";

char *t = NULL;

printf("ntest_1");
for( t = tokenizer( test_1, "\x") ; t ; t = tokenizer( NULL, "\x")) {
printf("ntoken: %s", t);
}
printf("n");



J'ai comme l'impression que l'API implementée ci-dessus est utilisée à tort
et à travers ici. la chaine "\x" passée à tokenizer va être interprétée
par cette dernière comme une chaine de séparateurs possibles, pas comme un
séparateur de plusieurs caractères... on va donc s'arrêter sur n'importe
quelle séquence de caractères '\' et/ou 'x'

printf("ntest_2");
for( t = tokenizer( test_2, "\x") ; t ; t = tokenizer( NULL, "\x")) {
printf("ntoken: %s", t);
}
printf("n");

return 0;
}

reste à convertir en hexa et à placer dans un tableau de bytes... C TOUT.



Non, cette proposition n'est pas une bonne base pour le problème posé, quant
à l'élégance, on en est fort loin.

Voici une fonction qui parse la chaine en poussant un pointeur :

/* pp est l'adresse d'un pointeur const char * qui sera avancé à la position
suivante en cas de succès.
la fonction retourne la valeur de l'octet encodé ou -1 en fin de chaine ou
si l'encodage est incorrect (pas de x, pas d'encodage hexadecimal, trop de
chiffres...)
*/

#include #include #include
int getbyte(const char **pp) {
const char *p = *pp;

if (p[0] == '\' && p[1] == 'x' && isxdigit((unsigned char)p[2]) &&
(!isxdigit((unsigned char)p[3]) || !isxdigit((unsigned
char)p[4]))) {
return (int)strtoul(p + 2, (char **)pp, 16);
}
return -1;
}


/* Voici un exemple d'utilisation: */

void parse(const char *p) {
int c;

printf("parsing "%s"n", p);
while ((c = getbyte(&p)) >= 0) {
printf("got %dn", c);
}
if (*p) {
printf("parsing stopped at "%s"n", p);
}
}

int main() {
parse("\x2\x52\x20\x71");
parse("\x2\x0\x");
parse("\x001");
parse("\x123");
parse("\x 1");
parse("\x+1");
parse("\x+12");
parse("\x-1");
return 0;
}

Ce n'est pas particulièrement élégant à cause des idiosyncrasies de la
librairie standard.
On peut aussi utiliser sscanf, qu'en règle générale je déconseille à cause
des nombreux pièges qu'elle recelle :

int getbyte(const char **pp) {
unsigned int value;
int n;

if (sscanf(*pp, "\x%2x%n", &value, &n) == 1) {
*pp += n;
return value;
}
return -1;
}

Cette solution moins efficace accepte aussi les séquences "x 1", "x+1",
"x-1" et le diagnostique sur les séquences trop longues est incorrect.

Pour une solution élégante, il faut changer de langage et utiliser des
expressions régulières.

--
Chqrlie.
Publicité
Poster une réponse
Anonyme