Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

istringstream et parsage fichier

10 réponses
Avatar
Korchkidu
Bonjour,

j'essaie de faire un parser de fichier. J'utilise istringstream car je
trouve ca super pratique et simple. Mais j'ai un petit probleme. J'ai
un fichier qui contient des lignes remplis de double. Chaque valeur
est separee de la suivante par un tab:
<valeur1><tab><valeur2><tab><valeur3><tab><valeur4><tab><valeur5><tab>...
Si je fais
getline(inputfile, line);
std::istringstream in(line);
in>>v1>>v2>>v3>>v4>>v5;
ca marche.... normal...

Cependant, il arrive que parfois, certaines valeurs ne soient pas
renseignees. Du coup, j'ai un truc comme:
<valeur1><tab><tab><tab><valeur4><tab><valeur5><tab>...
du coup, si je fais la meme chose:
in>>v1>>v2>>v3>>v4>>v5;
Bah ca marche plus.... normal car il ignore les tabs.... Comment me
debrouiller pour que v2 et v3 aient une valeur par defaut (0) plutot
que les valeurs qui normalement devraient etre assignees a v4 et v5 ?

Merci d'avance pour votre aide.
K.

10 réponses

Avatar
James Kanze
On Apr 26, 5:55 pm, Korchkidu wrote:

j'essaie de faire un parser de fichier. J'utilise istringstream car je
trouve ca super pratique et simple. Mais j'ai un petit probleme. J'ai
un fichier qui contient des lignes remplis de double. Chaque valeur
est separee de la suivante par un tab:
<valeur1><tab><valeur2><tab><valeur3><tab><valeur4><tab><valeur5><tab>...
Si je fais
getline(inputfile, line);
std::istringstream in(line);
in>>v1>>v2>>v3>>v4>>v5;
ca marche.... normal...

Cependant, il arrive que parfois, certaines valeurs ne soient pas
renseignees. Du coup, j'ai un truc comme:
<valeur1><tab><tab><tab><valeur4><tab><valeur5><tab>...
du coup, si je fais la meme chose:
in>>v1>>v2>>v3>>v4>>v5;
Bah ca marche plus.... normal car il ignore les tabs.... Comment me
debrouiller pour que v2 et v3 aient une valeur par defaut (0) plutot
que les valeurs qui normalement devraient etre assignees a v4 et v5 ?


Deux possibilités :

-- Une fois que tu as lu la ligne, tu utilises boost::regex
pour le découper en champs ; tu n'utilises istringstream
que sur les champs individuels.

C'est une solution générique, qui peut s'appliquer à à peu
près n'importe quel format (mais c'est peut-être le marteau
pilon pour écraser la mouche ici).

-- Tu lis un double à la fois, dans une boucle, avec un test
avant la lecture chaque fois. Quelque chose du genre :

for ( int i = 0 ; in && i < 5 ; ++ i ) {
if ( in.peek() == 't' ) {
result[ i ] = 0.0 ;
in.get() ; // extraire le 't'.
} else {
in >> result[ i ] ;
}
}

C'est probablement la plus simple dans ton cas.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
James Kanze
On Apr 26, 5:55 pm, Korchkidu wrote:

j'essaie de faire un parser de fichier. J'utilise istringstream car je
trouve ca super pratique et simple. Mais j'ai un petit probleme. J'ai
un fichier qui contient des lignes remplis de double. Chaque valeur
est separee de la suivante par un tab:
<valeur1><tab><valeur2><tab><valeur3><tab><valeur4><tab><valeur5><tab>...
Si je fais
getline(inputfile, line);
std::istringstream in(line);
in>>v1>>v2>>v3>>v4>>v5;
ca marche.... normal...

Cependant, il arrive que parfois, certaines valeurs ne soient pas
renseignees. Du coup, j'ai un truc comme:
<valeur1><tab><tab><tab><valeur4><tab><valeur5><tab>...
du coup, si je fais la meme chose:
in>>v1>>v2>>v3>>v4>>v5;
Bah ca marche plus.... normal car il ignore les tabs.... Comment me
debrouiller pour que v2 et v3 aient une valeur par defaut (0) plutot
que les valeurs qui normalement devraient etre assignees a v4 et v5 ?


Deux possibilités :

-- Une fois que tu as lu la ligne, tu utilises boost::regex
pour le découper en champs ; tu n'utilises istringstream
que sur les champs individuels.

C'est une solution générique, qui peut s'appliquer à à peu
près n'importe quel format (mais c'est peut-être le marteau
pilon pour écraser la mouche ici).

-- Tu lis un double à la fois, dans une boucle, avec un test
avant la lecture chaque fois. Quelque chose du genre :

for ( int i = 0 ; in && i < 5 ; ++ i ) {
if ( in.peek() == 't' ) {
result[ i ] = 0.0 ;
in.get() ; // extraire le 't'.
} else {
in >> result[ i ] ;
}
}

C'est probablement la plus simple dans ton cas.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Laurent Deniau
Korchkidu wrote:
Bonjour,

j'essaie de faire un parser de fichier. J'utilise istringstream car je
trouve ca super pratique et simple. Mais j'ai un petit probleme. J'ai
un fichier qui contient des lignes remplis de double. Chaque valeur
est separee de la suivante par un tab:
<valeur1><tab><valeur2><tab><valeur3><tab><valeur4><tab><valeur5><tab>...
Si je fais
getline(inputfile, line);
std::istringstream in(line);


Et si tu utilises:

in.noskipws();

ca marche.... normal...

Cependant, il arrive que parfois, certaines valeurs ne soient pas
renseignees. Du coup, j'ai un truc comme:
<valeur1><tab><tab><tab><valeur4><tab><valeur5><tab>...
du coup, si je fais la meme chose:
in>>v1>>v2>>v3>>v4>>v5;


avec

in>>v1>>c1>>v2>>c2>>v3>>c3>>v4>>c4>>v5>>c5;

?

Bah ca marche plus.... normal car il ignore les tabs.... Comment me
debrouiller pour que v2 et v3 aient une valeur par defaut (0) plutot
que les valeurs qui normalement devraient etre assignees a v4 et v5 ?


a+, ld.

Avatar
James Kanze
On Apr 27, 9:34 am, Laurent Deniau wrote:
Korchkidu wrote:

j'essaie de faire un parser de fichier. J'utilise istringstream car je
trouve ca super pratique et simple. Mais j'ai un petit probleme. J'ai
un fichier qui contient des lignes remplis de double. Chaque valeur
est separee de la suivante par un tab:
<valeur1><tab><valeur2><tab><valeur3><tab><valeur4><tab><valeur5><tab>. ..
Si je fais
getline(inputfile, line);
std::istringstream in(line);


Et si tu utilises:

in.noskipws();


Ce n'est pas vraiment nécessaire dans son cas, parce qu'il doit
régarder avant d'essayer la conversion.

ca marche.... normal...

Cependant, il arrive que parfois, certaines valeurs ne soient pas
renseignees. Du coup, j'ai un truc comme:
<valeur1><tab><tab><tab><valeur4><tab><valeur5><tab>...
du coup, si je fais la meme chose:
in>>v1>>v2>>v3>>v4>>v5;


avec

in>>v1>>c1>>v2>>c2>>v3>>c3>>v4>>c4>>v5>>c5;


Et que se passe-t-il s'il a un champs qui manque ? Du coup, le
flux se met en erreur, il ne lit pas les valeurs suivantes, et
on ne peut même pas savoir combien de champs il a lu.

Je crois que tu as mal compris son cahier de charges. D'après ce
que j'ai compris, si un champs manque, il faut se servir d'une
valeur par défaut, et non le traiter comme une erreur.

Remarque, ta solution marche si on utilise un type wrapper pour
les doubles, dont l'opérateur << sait utiliser une valeur par
défaut s'il ne trouve pas de valeur dans le flux.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Sylvain
James Kanze wrote on 27/04/2007 18:46:

Cependant, il arrive que parfois, certaines valeurs ne soient pas
renseignees. Du coup, j'ai un truc comme:
<valeur1><tab><tab><tab><valeur4><tab><valeur5><tab>...
du coup, si je fais la meme chose:
in>>v1>>v2>>v3>>v4>>v5;


avec

in>>v1>>c1>>v2>>c2>>v3>>c3>>v4>>c4>>v5>>c5;


Et que se passe-t-il s'il a un champs qui manque ? Du coup, le
flux se met en erreur, il ne lit pas les valeurs suivantes, et
on ne peut même pas savoir combien de champs il a lu.


un opérateur istringstream& >> (istringstream&, double&) qui testerait
si un et un seul caractère séparateur est présent avant un nombre valide
et qui affecterait au double opérande une valeur par défaut si cette
condition n'est pas vérifiée pourrait donner l'impression de marcher -
il faudra à tout le moins que l'extracteur ne consomme qu'un séparateur
et puisse tester sans le consommer le caractère suivant du flux.

extraire séparément les séparateurs n'apporte rien et empêchera plutôt
le traitement par perte de l'information "2 séparateurs consécutifs".

Sylvain.



Avatar
James Kanze
On Apr 27, 10:39 pm, Sylvain wrote:
James Kanze wrote on 27/04/2007 18:46:

Cependant, il arrive que parfois, certaines valeurs ne soient pas
renseignees. Du coup, j'ai un truc comme:
<valeur1><tab><tab><tab><valeur4><tab><valeur5><tab>...
du coup, si je fais la meme chose:
in>>v1>>v2>>v3>>v4>>v5;


avec

in>>v1>>c1>>v2>>c2>>v3>>c3>>v4>>c4>>v5>>c5;


Et que se passe-t-il s'il a un champs qui manque ? Du coup, le
flux se met en erreur, il ne lit pas les valeurs suivantes, et
on ne peut même pas savoir combien de champs il a lu.


un opérateur istringstream& >> (istringstream&, double&)


Existe déjà. Il faut utiliser un type proxy autour de
double :

std::istream&
operator>>( std::istream& source,
DelimitedDouble const& destProxy )

qui testerait
si un et un seul caractère séparateur est présent avant un nombre v alide
et qui affecterait au double opérande une valeur par défaut si cette
condition n'est pas vérifiée pourrait donner l'impression de marcher -
il faudra à tout le moins que l'extracteur ne consomme qu'un séparate ur
et puisse tester sans le consommer le caractère suivant du flux.


C'est exact. Il pourrait donner l'air de marcher s'il marche.
En fait, il ne serait pas trop difficile de l'écrire :

std::istream&
operator>>( std::istream& source,
DelimitedDouble const& destProxy )
{
if ( source.peek() == 't' ) {
source.get() ;
destProxy = 0.0L ;
} else {
double tmp ;
source >> tmp ;
if ( source ) {
destProxy = tmp ;
}
}
return *this ;
}

avec

class DelimitedDouble
{
public:
explicit DelimitedDouble( double& dest )
: myDest( dest )
{
}
void operator=( double other ) const
{
myDest = other ;
}

private:
double& myDest ;
} ;


extraire séparément les séparateurs n'apporte rien et
empêchera plutôt le traitement par perte de l'information "2
séparateurs consécutifs".


Pas forcement. On peut très bien le faire des deux façons, selon
ses goûts.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
Jean-Marc Bourguet
James Kanze writes:

un opérateur istringstream& >> (istringstream&, double&)


Existe déjà. Il faut utiliser un type proxy autour de
double :


Il n'existe pas déjà, mais introduire un operateur qui prend un
istringstream& qui fait autre chose que celui qui prend un istream& me
semble être un bon moyen d'introduire de la confusion (le bug qui aura lieu
quand on modifiera des appels chainés de sorte qu'on changera l'opérateur
appelé dans du code qui fonctionnait sera bien amusant).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Sylvain
James Kanze wrote on 28/04/2007 13:32:

un opérateur istringstream& >> (istringstream&, double&)


Existe déjà. Il faut utiliser un type proxy autour de
double :


j'ai dit "un" opérateur, pas "l"'opérateur :)
j'ignore (pour ne pas l'utiliser) ce qu'est un "istringstream", je
voulais seulement indiquer ici "flux supportant l'extraction de caractères".

C'est exact. Il pourrait donner l'air de marcher s'il marche.
En fait, il ne serait pas trop difficile de l'écrire :

std::istream&
operator>>( std::istream& source,
DelimitedDouble const& destProxy )
{
if ( source.peek() == 't' ) {
source.get() ;
destProxy = 0.0L ;
} else {
double tmp ;
source >> tmp ;
if ( source ) {
destProxy = tmp ;
}
}
return *this ;
}


je voyais plutôt:

// le flux contient à la pos. courante:
// <tab><double>{...} pour un nb présent
// <tab>EOL|<tab>... pour un nb absent

if ( source.get() != 't' )
throw bad_format();

// do more characters present?
if (source.eol() || source.peek() == 't')
destProxy = defaultValue;
else
source >> destProxy;

extraire séparément les séparateurs n'apporte rien et
empêchera plutôt le traitement par perte de l'information "2
séparateurs consécutifs".


Pas forcement. On peut très bien le faire des deux façons, selon
ses goûts.


il me parait plus aisé de considérer et d'extraire le tab. dans le
traitement d'extraction du double; si le flux de base s'amuse à "trimer"
les tabs en début / fin et si les extracteurs de nombres s'amusent à
jeter sans les compter / traiter les tabs présents devant / après un
digit, tu ne pourras pas savoir si un champ est non présent et doit être
initialisé avec une valeur par défaut - d'où mon traitement ci-avant.

Sylvain.


Avatar
James Kanze
On Apr 29, 5:36 pm, Sylvain wrote:
James Kanze wrote on 28/04/2007 13:32:


[...]
// le flux contient à la pos. courante:
// <tab><double>{...} pour un nb présent
// <tab>EOL|<tab>... pour un nb absent


Intéressant. C'est effectivement un autre approche, qui part
d'une autre façon de concevoir le format. Je suis parti d'une
conception où un caractère 't' (et un seul) était le séparateur
de champs. Mais c'est vrai que rien n'empêche de considérer le
format d'un champs comme étant un 't', suivi optionnellement
d'un double (et qu'il n'y a pas de séparateur de champs, parce
qu'il n'y en a pas besoin). Éventuellement, il faudrait traiter
le premier champ différemment, parce qu'il ne commence pas par
un 't'.

En tout cas, considérer le problème d'un autre point de vue m'a
fait rendre compte d'une erreur dans ma suggestion. Si le 't'
est un séparateur, il ne faut pas l'extraire du flux, même dans
le cas où on utilise la valeur par défaut, parce qu'évidemment,
on extrait les séparateurs séparamment.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Korchkidu
Merci a tous pour vos reponses, ca marche super.

A+,
K.