Extraire des données avec sscanf

Voir le sujet précédent Voir le sujet suivant Aller en bas

Extraire des données avec sscanf

Message  Aurek le Dim 26 Oct 2008 - 19:42

Bonjour à tous,

J'ai quelques problèmes avec la fonction sscanf. J'ai une chaîne formatée ainsi : unsigned short int unsigned short int char char * le problème est que les données extraites ne sont pas en accord avec ce que j'attends. En effet, si je prends la chaîne suivante : 70jBonjour. Je m'attends à avoir 7 0 j Bonjour dans mes variables mais le problème est que j'ai 70 dans la première variable et ensuite n'importe quoi dans les autres.

Comment faire ?

Merci d'avance.

Aurek

Messages : 4
Date d'inscription : 26/10/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  Aurek le Lun 27 Oct 2008 - 10:14

J'ai trouvé une solution qui n'utilise pas sscanf donc, je la propose afin de savoir si elle est correct.

A la base, je voulais utilisé ce procéder (sscanf) pour récupérer les données d'une trame envoyée par mon client à mon serveur. Mais par un détour sur le forum de developpez.net j'ai réussi à trouver une solution que j'ai mise en place mais est-elle portable ?

Ce que j'ai fait :

Structure de ma Trame :

Code:
struct TrameHead
{
  unsigned short int taille;
  unsigned short int commande;
  char code;
};

struct Trame
{
  TrameHead head;
 
  /* Liste des arguments de la trame */
  char args[256];
};

Code:
recv(socket, (char *)&trame, sizeof(trame) ,0);
en ayant bien sûr fait
Code:
send(fd_socket, (char *)trame, sizeof(*trame), 0)

Merci d'avance.

Aurek

Messages : 4
Date d'inscription : 26/10/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  -ed- le Lun 27 Oct 2008 - 10:31

Aurek a écrit:J'ai quelques problèmes avec la fonction sscanf. J'ai une chaîne formatée ainsi : unsigned short int unsigned short int char char * le problème est que les données extraites ne sont pas en accord avec ce que j'attends. En effet, si je prends la chaîne suivante : 70jBonjour. Je m'attends à avoir 7 0 j Bonjour dans mes variables mais le problème est que j'ai 70 dans la première variable et ensuite n'importe quoi dans les autres.
sscanf() est fait pour des chaines formatées, c'est à dire avec avec un séparateur bien précis entre les champs :
Code:
70 jBonjour
70,jBonjour
70/jBonjour
etc.

(à moins que 'j' ne soit le séparateur ?)

Pour faire ce que tu veux, il va falloir le faire à la main. Encore faut-il que le format soit définit et régulier.

Soit il est de largeur fixe :

<chiffre><chiffre><texte>

avec :

chiffre ::= 0-9
texte ::= TEXT


Soit il faut un séparateur comme indiqué ci-dessus.

Alors, quel est le format du fichier ?

Nota : cependant, si le but est de récupérer une valeur numérique codée en décimal (largeur variable), puis un texte, on peut utiliser strtol(). Le 2 ème paramètre pointera alors sur le texte.
Code:

#include <stdio.h>

int main (void)
{
  char const *s = "70jBonjour";
  char *p;
  long n;

  n = strtol (s, &p, 10);

  printf ("n = %ld\n", n);
  printf ("p = '%s'\n", p);

  return 0;
}


-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  -ed- le Lun 27 Oct 2008 - 10:43

Aurek a écrit:J'ai trouvé une solution qui n'utilise pas sscanf donc, je la propose afin de savoir si elle est correct.

A la base, je voulais utilisé ce procéder (sscanf) pour récupérer les données d'une trame envoyée par mon client à mon serveur. Mais par un détour sur le forum de developpez.net j'ai réussi à trouver une solution que j'ai mise en place mais est-elle portable ?
Ah, transfert de données entre client et serveur, donc par les sockets.
- Quel est le format des données (texte, binaire )
- Je rappelle que sscanf() ne fonctionne que sur du texte et plus particulièrement, une chaine C correctement formée.

Ce que j'ai fait :

Structure de ma Trame :

Code:
struct TrameHead
{
  unsigned short int taille;
  unsigned short int commande;
  char code;
};

struct Trame
{
  TrameHead head;
 
  /* Liste des arguments de la trame */
  char args[256];
};

Code:
recv(socket, (char *)&trame, sizeof(trame) ,0);
en ayant bien sûr fait
Code:
send(fd_socket, (char *)trame, sizeof(*trame), 0)
Ce n'est pas du tout portable.

Des éléments de réponse se trouvent ici :

http://mapage.noos.fr/emdel/notes.htm#enreg_struct
http://mapage.noos.fr/emdel/reseaux.htm#texte

Etudie tout ça, et fait de ton mieux. Poste ton code.

Les solutions binaires officielles et normalisées sont complexes, mais une solution 'binaire simple' portable est possible. Elle nécessite une spécification en béton, notamment concernant le codage des valeurs numériques.

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  Aurek le Lun 27 Oct 2008 - 11:14

Après lecture des deux articles cités, je ne suis pas plus avancé. J'ai lu que la solution binaire la plus simple est le TLV mais le problème est qu'en soit c'est déjà un formatage ?

Dans mon cas, j'ai déjà obligation du format de mes trames. Ce format est le suivant :

LLIICAAAAAAAAAAAAA
LL : 2 octets pour ayant pour valeur la longueur total de la trame
II : un identifiant de trame sur 2 octets
C : code de la commande sur 1 octet
AA... : arguments de la commande sur T - 5 octets où T est la taille de la tram

Les trames ne peuvent faire plus de 256 octets. La taille de l'entête est fixe à 5 octets mais les arguments eux peuvent varier.

En plus de tout ça, ce protocole a été définie en cours pour toute la classe. Il faut garder un encodage qui permette d'obtenir quelque chose de portable quelque soit le serveur utilisé.

Aurek

Messages : 4
Date d'inscription : 26/10/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  -ed- le Lun 27 Oct 2008 - 11:33

Aurek a écrit:Dans mon cas, j'ai déjà obligation du format de mes trames. Ce format est le suivant :

LLIICAAAAAAAAAAAAA
LL : 2 octets pour ayant pour valeur la longueur total de la trame
II : un identifiant de trame sur 2 octets
C : code de la commande sur 1 octet
AA... : arguments de la commande sur T - 5 octets où T est la taille de la tram

Les trames ne peuvent faire plus de 256 octets. La taille de l'entête est fixe à 5 octets mais les arguments eux peuvent varier.

En plus de tout ça, ce protocole a été définie en cours pour toute la classe. Il faut garder un encodage qui permette d'obtenir quelque chose de portable quelque soit le serveur utilisé.
OK. Le principe est d'utiliser un tableau de unsigned char dans lequel chaque élément correspond à un champ bien défini.

Tu n'as pas précisé la manière dont les données numériques sont codées. Je vais supposer que c'est le format habituel 'réseau', c'est à dire MSB en tête.

La trame est donc spécifiée ainsi (octets par octets):

[0] Longueur de la trame (MSB)
[1] Longueur de la trame (LSB)
[2] Identifiant de la trame (MSB)
[3] Identifiant de la trame (LSB)
[4] Code de commande
[5]...[255] Données sur une longueur de Taille de la trame - 5.

On définit donc un tableau de 256 unsigned char que l'on rempli comme i faut :

Soit à transmettre :

Identifiant = 1234
Code = 'X'
Données = "Hello world"

On écrit une fonction d'émission qui a donc pour interface :

Code:
int envoyer (SOCKET sock, int identifiant, int code, void const *data, size_t size);
et que l'on appelle ainsi :
Code:
 
char const *message = "Hello world";
envoyer (sock, 1234, 'X', message, strlen (message));
Dans la fonction, on définit un tableau de 256 unsigned char que l'on remplit 'à la main' intelligemment en respectant la spécification, et que l'on émet ensuite classiquement et en une fois avec send(sock, trame, longueur, 0);

Je te laisse écrire ce code.

En réception, on fait le contraire.
Code:
int recevoir (SOCKET sock, int *p_identifiant, int *p_code, void *data, size_t size);
Si on veut, on peut regrouper tout ou partie des données dans une structure interne, ça facilite le codage. Je te laisse envisager les différentes solutions...

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  Aurek le Lun 27 Oct 2008 - 23:53

Voilà le résultat des mes recherches !

D'une part j'ai conservé la structure Trame car elle me facilite grandement le travail j'ai donc :

Code:
struct TrameHead
{
  unsigned short int taille;
  unsigned short int id;
  char code;
};

struct Trame
{
  TrameHead head;
 
  /* Liste des arguments de la trame */
  char args[251];
};


Je mets en place mon tableau d'unsigned char :


Code:
unsigned char buff[256]; 
 
buff[0] = (trame->head.taille & 0xFF00) >> 8;
 
buff[1] = trame->head.taille & 0x00FF;
 
buff[2] = (trame->head.id & 0xFF00) >> 8;
 
buff[3] = trame->head.id & 0x00FF;

buff[4] = trame->head.code;
 
for(int i = 5; i < strlen(trame->args) + 5; ++i)
  buff[i] = trame->args[i - 5];

Je l'envoie ainsi :

Code:
send(fd_socket, buff, trame->head.taille, 0)

Et je récupère cette trame ainsi :

Code:
Trame trame;
char args[256];
 
trame.head.taille = buff[0] | buff[1]; 
trame.head.id    = buff[2] | buff[3]; 
trame.head.code  = buff[4];

int i;

for(i = 5; i < trame.head.taille; ++i)
  trame.args[i - 5] = buff[i];
   
trame.args[i] = 0;

J'espère que ce code est plus portable que le précédent.

Question : Dans le cas présent, on fait l'hypotèse que les deux machines où sont lancées le client et le serveur sont en big-endian ?

Merci pour l'aide !

Aurek

Messages : 4
Date d'inscription : 26/10/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  -ed- le Mar 28 Oct 2008 - 1:37

Aurek a écrit:
D'une part j'ai conservé la structure Trame car elle me facilite grandement le travail j'ai donc :
En interne uniquement. Pas pour l'interface réseau évidemment.
Code:
struct TrameHead
{
  unsigned short int taille;
  unsigned short int id;
  char code;
};

struct Trame
{
  TrameHead head;
 
  /* Liste des arguments de la trame */
  char args[251];
};
Comme c'est en interne, tu peux simplifier. Le type int est plus naturel et génère moins de code machine parasite.
Code:
struct TrameHead
{
  int taille;
  int id;
  int code;
};

struct Trame
{
  TrameHead head;
 
  /* Liste des arguments de la trame */
  char args[251];
};
Je mets en place mon tableau d'unsigned char :
Code:
unsigned char buff[256];


OK
Code:

buff[0] = (trame->head.taille & 0xFF00) >> 8;
buff[1] = trame->head.taille & 0x00FF;
buff[2] = (trame->head.id & 0xFF00) >> 8;
buff[3] = trame->head.id & 0x00FF;
buff[4] = trame->head.code;
OK. Je préfère le code régulier, je fais mois d'erreurs. On peut faire le masquage à la fin :
Code:

buff[0] = (trame->head.taille >> (8 * 1)) && 0xFF; /* MSB en tete */
buff[1] = (trame->head.taille >> (8 * 0)) && 0xFF;
buff[2] = (trame->head.id >> (8 * 1)) && 0xFF; /* MSB en tete */
buff[3] = (trame->head.id >> (8 * 0)) && 0xFF;
buff[4] = trame->head.code && 0xFF;

Code:
for(int i = 5; i < strlen(trame->args) + 5; ++i)
  buff[i] = trame->args[i - 5];
Tu fais l'hypothèse que les données à transmettre sont une chaine de caractères C valide. C'est bien vrai dans tous les cas ?

Admettons. Là encore, inutilement complexe. Il faut suivre la définition et éviter les '-' sources d'erreurs diaboliques... Attention aux constructions non reconnues en C90... Éviter d'appeler une fonction dans une boucle...
Code:

int i;
int const len = strlen(trame->args);
for(i = 0; i <  len; ++i)
  buff[i + 5] = trame->args[i];

Je l'envoie ainsi :

Code:
send(fd_socket, buff, trame->head.taille, 0)
OK. 'espérons que 'taille' a la bonne valeur)
Et je récupère cette trame ainsi :

Code:
Trame trame;
char args[256];
 
trame.head.taille = buff[0] | buff[1]; 
trame.head.id    = buff[2] | buff[3]; 
trame.head.code  = buff[4];
Pas bon du tout... Il faut recomposer les entiers .taille et .id avec les décalages inverses...

Code:
int i;

for(i = 5; i < trame.head.taille; ++i)
  trame.args[i - 5] = buff[i];
   
trame.args[i] = 0;
Là encore, inutilement complexe. Tu connais ma solution...
J'espère que ce code est plus portable que le précédent.
Oui, il est portable (après correction de la réception).
Question : Dans le cas présent, on fait l'hypothèse que les deux machines où sont lancées le client et le serveur sont en big-endian ?
Non, pas du tout. C'est pour ça qu'il est portable.

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Extraire des données avec sscanf

Message  Contenu sponsorisé Aujourd'hui à 21:20


Contenu sponsorisé


Revenir en haut Aller en bas

Voir le sujet précédent Voir le sujet suivant Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum