OVH Cloud OVH Cloud

Analyse syntaxique

31 réponses
Avatar
Dominique MICOLLET
Bonjour,

Je cherche à isoler dans la chaîne suivante :
%%%{1,[2,0,0,0,0,2]%%%}+%%%{1,[1,0,0,1,0,2]%%%}
les différents nombres qui y apparaissent, en conservant leur sémantique (le
premier 1 est un coefficient, le premier 2 est un degré).

En utilisant astucieusement les méthodes de recherche de caractères ou
sous-chaînes et d'extraction de ces dernières, je devrais m'en sortir.

Mais j'imagine que d'autres ont déjà eu ce souci et qu'il existe une
solution toute prête.

Auriez des suggestions de pistes à suivre ?

--
Cordialement

Dominique MICOLLET Email : enlever deux fr
Universite de Bourgogne
9, Avenue Alain SAVARY BP 47870 Tel : +33/(0)3-80-39-59-27
21078 DIJON CEDEX FRANCE Tfx : +33/(0)3-80-39-68-69

10 réponses

1 2 3 4
Avatar
Marc Boyer
Le 27-02-2007, Dominique MICOLLET a écrit :
J'avais pensé au sscanf, mais comme je débute en C++, j'essaie d'éviter
d'employer des fonctions venant du monde C.


C'est un bon réflexe de débutant, mais ce n'est pas une
règle absolue.

J'ai du lire quelque part que
c'est mal d'employer des fonctions de C :-)


Parfois, des gens écrivent des bétises (ou des approximations).

Je cherche donc à employer l'outil C++ lorsqu'il existe.


Ils existent. De là à dire qu'ils sont plus efficaces dans
ce cas là. Mais bon, je ne demande qu'à être convaincu.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
Jean-Marc Bourguet
"James Kanze" writes:

Boost.Spirit ?


Là, c'est peut-être effectivement un peu lourd. (Surtout, je
crois, c'est encore un langage à apprendre.)


Mais non, c'est du C++.

.
.
.

:-)

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
Marc Boyer
Le 26-02-2007, Michel Decima a écrit :
On Mon, 26 Feb 2007 15:28:12 +0000 (UTC), Marc Boyer
:

string s= "%%%{1,[2,0,0,0,0,2]%%%}+%%%{1,[1,0,0,1,0,2]%%%}";
int res= scanf("%%%%%%{%d,[%d,%d,%d,%d,%d,%d]%%%%%%}+"
"%%%%%%{%d,[%d,%d,%d,%d,%d,%d]%%%%%%}",
&coef,&degre,
/// A continuer avec la sémantique de tes vars
C'est lourd, impossible à maintenir, et tout se casse la gueule si

jamais l'expression n'est pas fixe.


Sérieux ?
Et tu envisages quoi qui serait moins lourd, plus maintenable ?


Boost.Spirit ?


lex/yacc aussi tant qu'on y est. Ou une librairie xml...

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)




Avatar
James Kanze
On Feb 26, 4:29 pm, Marc Boyer
wrote:

On Feb 26, 9:09 am, Dominique MICOLLET
bourgogne.fr.fr.fr> wrote:

Je cherche à isoler dans la chaîne suivante :
%%%{1,[2,0,0,0,0,2]%%%}+%%%{1,[1,0,0,1,0,2]%%%}
les différents nombres qui y apparaissent, en conservant leur séma ntique (le
premier 1 est un coefficient, le premier 2 est un degré).

En utilisant astucieusement les méthodes de recherche de caractère s ou
sous-chaînes et d'extraction de ces dernières, je devrais m'en sor tir.

Mais j'imagine que d'autres ont déjà eu ce souci et qu'il existe u ne
solution toute prête.


La solution évidente, c'est boost::regex. Tu décris le format de
la chaîne en forme d'expression régulière, avec chacun des
nombres dans un (...). Ensuite, tu recupère les sous-chaînes
une à une, et tu te sers de std::istringstream pour les
convertir en valeurs numériques.


Est-ce que ce n'est pas le marteau pilon pour écraser la mouche ?


Je ne connais rien de plus simple pour valider une chaîne et en
extraire les champs. C'est l'outil de base.

Pourquoi pas un bête sscanf ?


Parce que je veux du code qui marche, et qui est facile à
maintenir ? Parce qu'il exige que le programmeur apprenne
encore un autre langage de description du format -- un langage
fort peu puissant, et en général inconnu des programmeurs C++ ?
Parce qu'il est extrèmement fragile en ce qui concerne les
paramètres, exigeant des pointeurs à des types bien précis ?
Parce qu'il ne donne pas beaucoup d'information en ce qui
concerne des erreurs éventuelles ?

C'est intéressant, mais je crois même à l'époque où je faisais
du C, je me suis jamais servir de scanf et companie, à cause de
tous leurs problèmes.

--
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 Feb 26, 6:11 pm, Michel Decima wrote:

On Mon, 26 Feb 2007 15:28:12 +0000 (UTC), Marc Boyer
:

string s= "%%%{1,[2,0,0,0,0,2]%%%}+%%%{1,[1,0,0,1,0,2]%%%}";
int res= scanf("%%%%%%{%d,[%d,%d,%d,%d,%d,%d]%%%%%%}+"
"%%%%%%{%d,[%d,%d,%d,%d,%d,%d]%%%%%%}",
&coef,&degre,
/// A continuer avec la sémantique de tes vars
C'est lourd, impossible à maintenir, et tout se casse la gueule si

jamais l'expression n'est pas fixe.


Sérieux ?
Et tu envisages quoi qui serait moins lourd, plus maintenable ?



Prèsque n'importe quoi. Côté maintenabilité : d'après la
sémantique qu'il a suggéré, ça ne m'étonnerait pas que certaines
des valeurs, voire toutes, soient des flottants. Alors,
attention entre les %f et les %lf. Sans parler des histoires de
localisation : s'il travaille avec une locale français (fort
probable vue la langue dans laquelle il a posté), les virgules
vont poser des problèmes s'il veut des flottants.

La solution évidente et intuitive, c'est des expressions
régulières : boost, de préférence, s'il n'utilise que des
compilateurs rélativement moderne qui le permet ; sinon, Posix
définit regcomp/regexec etc., dont il existe aussi des
implémentations libres si on travaille sous Windows.

Sinon, je créerai des manipulateurs spéciaux pour bouffer les
caractères de ponctuation (en vérifiant qu'on a ce qu'il faut),
et puis je me servirai de istringstream (aillant pris soin de
l'imbuer du locale "C"). (Un manipulateur qui cherche une chaîne
donnée doit être assez facile ; un bon exercice pour se mettre
aux manipulateurs.)

Boost.Spirit ?


Là, c'est peut-être effectivement un peu lourd. (Surtout, je
crois, c'est encore un langage à apprendre.)

--
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 Feb 27, 9:10 am, Marc Boyer
wrote:

J'avais pensé au sscanf, mais comme je débute en C++, j'essaie d' éviter
d'employer des fonctions venant du monde C.


C'est un bon réflexe de débutant, mais ce n'est pas une
règle absolue.


Et même. Quand il existe une bonne fonction C qui fait
exactement ce qu'on veut, et qu'on n'a pas de fonction C++ sous
la main.

J'ai du lire quelque part que
c'est mal d'employer des fonctions de C :-)


Parfois, des gens écrivent des bétises (ou des approximations).


Surtout, c'est assez difficile à éviter.

Je cherche donc à employer l'outil C++ lorsqu'il existe.


Ils existent. De là à dire qu'ils sont plus efficaces dans
ce cas là. Mais bon, je ne demande qu'à être convaincu.


Plus efficace pour le programmeur, il n'y a pas de doute. La
famille des scanf est une des plus difficiles à utiliser et à
maintenir ; je crois que je ne me suis jamais servi même quand
je faisais du C, à cause de sa fragilité.

--
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
Jean-Marc Bourguet wrote:
"James Kanze" writes:

Boost.Spirit ?
Là, c'est peut-être effectivement un peu lourd. (Surtout, je

crois, c'est encore un langage à apprendre.)


Mais non, c'est du C++.


C'est du C++ tres proche de BNF ;-)

a+, ld.



Avatar
Michel Decima
"James Kanze" writes:

Boost.Spirit ?
Là, c'est peut-être effectivement un peu lourd. (Surtout, je

crois, c'est encore un langage à apprendre.)


Mais non, c'est du C++.


C'est bien du C++, mais James a raison, Spirit est un sous-langage en
lui-meme. Mais bon, avec un peut d'habitude, on peut faire des choses
interessantes, parce que le systeme est completement extensible.

S'il s'agit juste d'analyser une seule chaine de caracteres, je veux
bien admettre que ca releve du pilonnage de mouche a l'arme lourde
(et je passe sur la lisibilité)...

#include <string>
#include <vector>
#include <boost/spirit.hpp>

using namespace std;
using namespace boost::spirit;
static char const* text "%%%{1,[2,0,0,0,0,2]%%%}+%%%{1,[1,0,0,1,0,2]%%%}";

struct Data { int x; vector<int> v; };
ostream& operator<<( ostream& dest, Data const& d )
{
dest << "x=" << d.x << " v=[ ";
for ( int i=0; i < d.v.size(); ++i )
dest << d.v[ i ] << " ";
dest << "]";
return dest;
}


void parse1() {
Data d1, d2;
char op = '_';

rule<> start_rule chseq_p("%%%{")
int_p[ assign_a( d1.x ) ] >> ch_p(',')
ch_p('[')
int_p[ push_back_a( d1.v ) ]
*( ch_p(',') >> int_p[ push_back_a( d1.v ) ] )
ch_p(']')
chseq_p("%%%}")
anychar_p[ assign_a( op ) ]
chseq_p("%%%{")
int_p[ assign_a( d2.x ) ] >> ch_p(',')
ch_p('[')
int_p[ push_back_a( d2.v ) ]
*( ch_p(',') >> int_p[ push_back_a( d2.v ) ] )
ch_p(']')
chseq_p("%%%}")
;



bool status = parse( text, start_rule ).full;

cout << "status: " << status << "n"
<< "d1: " << d1 << "n"
<< "op: " << op << "n"
<< "d2: " << d2 << "n";
}

Si maintenant on veut analyser plusieurs expressions, avec
eventuellement plusieurs operandes, on peut factoriser le parser
de Data dans une classe (et masquer sa complexite), et la fonction
d'analyse devient vraiment plus simple.

class Data_parser : public parser< Data_parser > {
public:
typedef Data_parser self_t;
typedef Data value_t;

template <typename ScannerT>
struct result {
typedef typename match_result<ScannerT, value_t>::type type;
};

template <typename ScannerT>
typename parser_result<self_t, ScannerT>::type
parse(ScannerT const& scan) const
{
typename ScannerT::iterator_t save = scan.first;
Data tmp;
rule<> start_rule chseq_p("%%%{")
int_p[ assign_a( tmp.x ) ] >> ch_p(',')
ch_p('[')
int_p[ push_back_a( tmp.v ) ]
*( ch_p(',') >> int_p[ push_back_a( tmp.v ) ] )
ch_p(']')
chseq_p("%%%}");



match<> found = start_rule.parse( scan );
if ( found )
return scan.create_match( found.length(), tmp, save,
scan.first);
else
return scan.no_match();
}
};

void parse2()
{
Data_parser data_p;
Data d1, d2;
char op;

rule<> start_rule data_p[ assign_a( d1 ) ]
anychar_p[ assign_a( op ) ]
data_p[ assign_a( d2 ) ];



bool status = parse( text, start_rule ).full;

cout << "status=" << status << "n"
<< "d1: " << d1 << "n"
<< "op: " << op << "n"
<< "d2: " << d2 << "n";
}

Bref, tout depend de ce qu'on a a faire. Si c'est l'analyse d'une chaine
une seule fois, j'utiliserais regexp. En revanche, s'il s'agit d'un
traitement frequent, et surtout si on dispose d'une grammaire EBNF,
alors je prends Spirit.



Avatar
Marc Boyer
Le 27-02-2007, James Kanze a écrit :
On Feb 26, 4:29 pm, Marc Boyer
wrote:

On Feb 26, 9:09 am, Dominique MICOLLET
bourgogne.fr.fr.fr> wrote:

Je cherche à isoler dans la chaîne suivante :
%%%{1,[2,0,0,0,0,2]%%%}+%%%{1,[1,0,0,1,0,2]%%%}
les différents nombres qui y apparaissent, en conservant leur sémantique (le
premier 1 est un coefficient, le premier 2 est un degré).

En utilisant astucieusement les méthodes de recherche de caractères ou
sous-chaînes et d'extraction de ces dernières, je devrais m'en sortir.

Mais j'imagine que d'autres ont déjà eu ce souci et qu'il existe une
solution toute prête.


La solution évidente, c'est boost::regex. Tu décris le format de
la chaîne en forme d'expression régulière, avec chacun des
nombres dans un (...). Ensuite, tu recupère les sous-chaînes
une à une, et tu te sers de std::istringstream pour les
convertir en valeurs numériques.


Est-ce que ce n'est pas le marteau pilon pour écraser la mouche ?


Je ne connais rien de plus simple pour valider une chaîne et en
extraire les champs. C'est l'outil de base.


Donc, sur le cas présent, tu vas faire
const std::string num= " *[-+]?[0-9]+.?[0-9]* *";
const std::string match_num= "("+num+")";
const std::string six_num= match_num + "," + match_num + ","
+ match_num + "," + match_num
+ match_num + "," + match_num;
const boost::regex e("%%%{"+match_num+"["+six_num+"]%%%}+%%%{"
+match_num+"["+six_num+"]%%%}";

Puis les
input>>coef>>degre;

Pourquoi pas un bête sscanf ?


Parce que je veux du code qui marche, et qui est facile à
maintenir ?


Tu compares le code C++ ci dessus avec le
sscanf( input, "%%%%%%%{%lf,[%lf,%lf,%lf,%lf,%lf,%lf]%%%%%%}+"
"%%%%%%%{%lf,[%lf,%lf,%lf,%lf,%lf,%lf]%%%%%%}",
&coef, &degre, ...);

Et tu trouve le C++ plus maintenable ?

Parce qu'il exige que le programmeur apprenne
encore un autre langage de description du format -- un langage
fort peu puissant, et en général inconnu des programmeurs C++ ?


OK, ça peut être un argument.

Parce qu'il est extrèmement fragile en ce qui concerne les
paramètres, exigeant des pointeurs à des types bien précis ?


M'enfin bon, les compilos en 2007 font la vérification de
type il me semble, non ?

Parce qu'il ne donne pas beaucoup d'information en ce qui
concerne des erreurs éventuelles ?


Je demande à voir la comparaison sur cet exemple.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)




Avatar
Marc Boyer
Le 27-02-2007, James Kanze a écrit :
On Feb 26, 6:11 pm, Michel Decima wrote:

On Mon, 26 Feb 2007 15:28:12 +0000 (UTC), Marc Boyer
:

string s= "%%%{1,[2,0,0,0,0,2]%%%}+%%%{1,[1,0,0,1,0,2]%%%}";
int res= scanf("%%%%%%{%d,[%d,%d,%d,%d,%d,%d]%%%%%%}+"
"%%%%%%{%d,[%d,%d,%d,%d,%d,%d]%%%%%%}",
&coef,&degre,
/// A continuer avec la sémantique de tes vars
C'est lourd, impossible à maintenir, et tout se casse la gueule si

jamais l'expression n'est pas fixe.


Sérieux ?
Et tu envisages quoi qui serait moins lourd, plus maintenable ?



Prèsque n'importe quoi. Côté maintenabilité : d'après la
sémantique qu'il a suggéré, ça ne m'étonnerait pas que certaines
des valeurs, voire toutes, soient des flottants. Alors,
attention entre les %f et les %lf.


Le compilo émet des avertissements il me semble.

Sans parler des histoires de
localisation : s'il travaille avec une locale français (fort
probable vue la langue dans laquelle il a posté), les virgules
vont poser des problèmes s'il veut des flottants.


Ca, c'est un vrai problème. Mais une fois identifié, il
se résoud assez vite (remplacer les , par des :, les %
par des # tant qu'on y est, et les . par des ,).

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)





1 2 3 4