IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Boost C++ Discussion :

Séparer une chaine sans token


Sujet :

Boost C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    730
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 730
    Par défaut Séparer une chaine sans token
    Bonjour.

    J'écris un petit convertisseur en ligne de commandes qui prend comme paramètre une concaténation de nombre et d'unités, du genre "3.1415㎭" ou "88mph" (sans espaces, sinon ce serait 2 arguments et non un seul).
    J'aimerai les séparer en 2 variables (de type double et std::string).

    Tous les codes sur le nets demandent un séparateur, mais il n'y en a pas ici.

    Voila ce que j'ai écrit, mais qui me m'affiche 2 tokens quand je lui demande de me donner l'unité (un vide, et l'autre correct).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    boost::regex number("\\d+[\\.,]?\\d*");
    std::string arg( argv[1] );
    boost::sregex_token_iterator tokens(arg.begin(), arg.end(), number, -1);
        //0 devrait séléctionner le nombre, et -1 l'unité
    boost::sregex_token_iterator regend;
        for (; tokens != regend; ++tokens)
            std::cout << *tokens << std::endl;
    Comment arriver à les séparer correctement et sans séparateur ?
    Merci d'avance.

  2. #2
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,

    Dès fois, je me demande si l'utilisation de regex ne complique pas plus le sujet qu'autre chose , mais bref

    En fait, il y a une petite incompréhension sur la manière dont les submatch (0, -1 ...) influe sur le résultat de sregex_token_iterator.

    Par défaut, si l'on ne passe pas pas de submatch, c'est comme si l'on passait 0, dans ce cas, le regex_token_iterator va tenter de trouver dans la chaine toutes les portions matchant entièrement la regex.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    std::regex number("\\d+[\\.,]?\\d*");
    std::string arg = "88 56.4 45";
     
    std::sregex_token_iterator tokens(arg.begin(), arg.end(), number);
    std::sregex_token_iterator regend;
    for (; tokens != regend; ++tokens)
        std::cout << *tokens << std::endl;
    Affiche :
    88
    56.4
    54

    Par contre si l'on passe -1 alors le std::sregex_token_iterator se comporte comme si la regex indiquait le *séparateur* à matcher :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    std::regex whitespace("\\s+");
    std::string arg = "88 56.4 45";
     
    std::sregex_token_iterator tokens(arg.begin(), arg.end(), whitespace, -1 );
    std::sregex_token_iterator regend;
     
    for (; tokens != regend; ++tokens)
      std::cout << *tokens << std::endl;
    Affiche :
    88
    56.4
    54

    Aucune des deux solutions n'est satisfaisante ici, car en fait on souhaite faire comme si l'on avait deux regex différentes une pour les valeurs (3.1415) et une pour les unités (rad). La solution est d'utiliser les submatchs : Un submatch est délimité par des parenthèses et permet dans une même regex de matcher les morceaux qui nous intéressent, par exemple ici on pourrait utiliser les submatch suivant :

    (\\d+[\\.,]?\\d*) -> la valeur
    (\\w+) -> l'unité

    Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    std::regex numberunit("(\\d+[\\.,]?\\d*)(\\w+)");
    std::string arg = "3.1415rad 88mph";
     
    std::sregex_token_iterator tokens(arg.begin(), arg.end(), numberunit, 1 ); // on choisit de ne garder que le submatch n°1
    std::sregex_token_iterator regend;
     
    for (; tokens != regend; ++tokens)
        std::cout << *tokens << std::endl;
    Affiche :
    3.1415
    88

    Mais ici on souhaite conserver les deux submatch, il faut alors passer au constructeur de sregex_token_iterator un tableau contenant l'index des submatch que l'on souhaite garder, c'est à dire 1 (la valeur) et 2 (l'unité)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    std::regex numberunit("(\\d+[\\.,]?\\d*)(\\w+)");
    std::string arg = "3.1415rad 88mph";
     
    int submatches[] = { 1, 2 };
    std::sregex_token_iterator tokens(arg.begin(), arg.end(), numberunit, submatches );
    std::sregex_token_iterator regend;
     
    for (; tokens != regend; ++tokens)
       std::cout << *tokens << std::endl;
    Affiche :
    3.1415
    rad
    88
    mph

    Et voilà

    Une remarque pour finir, on n'est pas obligé ici d'utiliser un sregex_token_iterator, on peut aussi utiliser directement la fonction regex_match, qui va remplir en une seule passe une structure smatch avec le résultat du match et des submatchs, au contraire de sregex_token_iterator qui découvre progressivement les matchs et submatchs à chaque incrémentation de l'itérateur.
    C'est peut être un plus plus pratique ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    std::regex numberunit("(\\d+[\\.,]?\\d*)(\\w+)");
    std::string arg = "3.1415rad";
    double valeur;
    std::string unite;
     
    std::smatch  res;
    if(regex_match(arg, res,  numberunit) == true)
    {
      if(res.size() == 3) // un match complet + deux submatch
      {
         valeur = stod(res[1]);
         unite = res[2];
      }
    }
    Note : res[0] contient 3.1415rad (le match complet)

  3. #3
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    730
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 730
    Par défaut
    Merci pour ta réponse claire et précise.

    Tiens je connaissais pas les submatch, décidément les regex sont des drôles de petits bonhommes

    Tout marche nickel (à part pour la conversion de stod non reconnu par mon compilo, mais c'est un autre problème).

    PS : smatch est dans boost:: et stod dans std::

  4. #4
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Ca fait quand meme usine a gaz. J'utiliserais strtod avec son second argument non nul, meme si une expression reguliere valide un peu plus le format. Les espaces dans les arguments sont rares.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    string unit;
    char* end;
    // errno = 0; // necessaire si on veut savoir s'il y a un overflow ou un underflow
    double v = strtod(argv[1], &end);
    if (v == 0 && end == argv[1]) {
       // ne commence pas par un nombre
    } else {
       // on peut s'amuser a trouver s'il y a un overflow ou un underflow, mais la valeur de v
       // est raisonnable (0 ou plus ou moins infini en pratique).
       unit = end;
    }
    Edit: En passant, stod est du C++11 et a un second parametre qui permet de faire la meme chose. Son probleme est qu'il leve une exception en case d'underflow ou d'overflow; je me demande si ignorer ces conditions n'est pas souvent plus adequat.

  5. #5
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    730
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 730
    Par défaut
    C'est vrai que ta solution a l'air plus simple pour mon problème, même si c'est du C-style.
    Par contre, la solution d'Arzar sera plus adéquate à des problèmes plus complexe, grâce au système de submatch.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Séparer une chaine de caractères sans split
    Par ndsaerith dans le forum Servlets/JSP
    Réponses: 4
    Dernier message: 24/05/2011, 16h05
  2. Réponses: 15
    Dernier message: 23/01/2006, 04h35
  3. Réponses: 9
    Dernier message: 30/11/2005, 18h18
  4. Séparer une chaine en 2
    Par zoubidaman dans le forum SQL
    Réponses: 6
    Dernier message: 15/06/2005, 23h11
  5. Séparer une chaine en sous chaine
    Par firemax dans le forum C
    Réponses: 9
    Dernier message: 03/06/2005, 12h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo