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

 C++ Discussion :

Manipuler des string


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut Manipuler des string
    Salut !

    J'essaye de faire un convertisseur de chiffres romains vers les chiffres arabes (ex: IV = 4), cependant, il y a certaines choses que je ne sais pas faire, je souhaite diviser la variable en autant de partie qu'il y a de lettre pour les identifier, comment puis-je faire cela ? Et également, en c++ je crois qu'il n'y a pas de fonction native pour vérifier qu'une string ne contient que des caracteres, je voudrais également vérifier qu'elle ne contient que les chiffres romains (I, V, X, L, C, D, M).

    Une petite question aussi, comment gerer les soustractions dans ce système ?
    Ca fait un moment que je ne programme plus je suis très rouillé.

    Je vous serais vraiment reconnaissant de m'aiguiller dans ce petit projet !

    Merci !

  2. #2
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Hello,

    Un string est un tableau : tu peux accéder à chaque caractère séparément avec un indice
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::string str = "foobar";
    char c = str[3]; // == 'b'
    Tu peux tester une condition sur un ensemble avec std::all_of.
    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
    16
    bool isRoman(char c) {
         char cc = toupper(c);
         return cc == 'I' || cc == 'V' || .... ;
     
         // ou
         char cc = toupper(c);
         static auto tokens = {'I', 'V', 'X', 'L', 'C'};
         return std::find(tokens.begin(), tokens.end(), cc) != tokens.end();
    }
    std::string str = "ivc";
    std::string str2 = "blah0";
     
    using std::begin;
    using std::end;
    bool strValid = std::all_of(begin(str), end(str), isRoman); // == true
    bool str2Valid = std::all_of(begin(str2), end(str2), isRoman); // == false
    edit : et la conversion peut se faire avec un std::for_each. Je te laisse chercher le foncteur qui va bien.

    A propos de cette ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return std::find(tokens.begin(), tokens.end(), cc) != tokens.end();
    J'ai lu un peu partout qu'il était préférable d'utiliser std::begin(container) au lieu de container.begin(), car plus facilement remplaçable et donc plus générique.
    VS2012 n'aime pas, il est perdu
    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
    16
    17
    18
    19
    error C2668: 'std::begin' : ambiguous call to overloaded function
    1>          c:\program files (x86)\microsoft visual c++ compiler nov 2012 ctp\include\initializer_list(52): could be 'const E *std::begin<char>(std::initializer_list<E>)' [found using argument-dependent lookup]
    1>          with
    1>          [
    1>              E=char
    1>          ]
    1>          c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility(868): or       'const E *std::begin<std::initializer_list<E>>(const _Container &)' [found using argument-dependent lookup]
    1>          with
    1>          [
    1>              E=char
    1>  ,            _Container=std::initializer_list<char>
    1>          ]
    1>          c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility(862): or       'const E *std::begin<std::initializer_list<E>>(_Container &)' [found using argument-dependent lookup]
    1>          with
    1>          [
    1>              E=char
    1>  ,            _Container=std::initializer_list<char>
    1>          ]
    1>          while trying to match the argument list '(std::initializer_list<char>)'
    Est-ce normal (construction automatique d'un vector ou autre via un initializer-list par exemple), ou VS déconne ?

  3. #3
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut
    Merci pour ton aide

    Citation Envoyé par Iradrille Voir le message
    edit : et la conversion peut se faire avec un std::for_each. Je te laisse chercher le foncteur qui va bien.

    A propos de cette ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return std::find(tokens.begin(), tokens.end(), cc) != tokens.end();
    J'ai lu un peu partout qu'il était préférable d'utiliser std::begin(container) au lieu de container.begin(), car plus facilement remplaçable et donc plus générique.
    VS2012 n'aime pas, il est perdu
    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
    16
    17
    18
    19
    error C2668: 'std::begin' : ambiguous call to overloaded function
    1>          c:\program files (x86)\microsoft visual c++ compiler nov 2012 ctp\include\initializer_list(52): could be 'const E *std::begin<char>(std::initializer_list<E>)' [found using argument-dependent lookup]
    1>          with
    1>          [
    1>              E=char
    1>          ]
    1>          c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility(868): or       'const E *std::begin<std::initializer_list<E>>(const _Container &)' [found using argument-dependent lookup]
    1>          with
    1>          [
    1>              E=char
    1>  ,            _Container=std::initializer_list<char>
    1>          ]
    1>          c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility(862): or       'const E *std::begin<std::initializer_list<E>>(_Container &)' [found using argument-dependent lookup]
    1>          with
    1>          [
    1>              E=char
    1>  ,            _Container=std::initializer_list<char>
    1>          ]
    1>          while trying to match the argument list '(std::initializer_list<char>)'
    Est-ce normal (construction automatique d'un vector ou autre via un initializer-list par exemple), ou VS déconne ?
    car begin() et begin(string a) sont different je pense.
    J'ai la même erreur donc.
    Apres je me souviens plus trop des differences entre container.function(), function(container), et container->function().

    EDIT:

    J'ai un petit soucis:

    C:\Users\PC-HP\Desktop\chiffre\romain\main.cpp||In function 'int main()'
    C:\Users\PC-HP\Desktop\chiffre\romain\main.cpp|32|error: 'all_of' was not declared in this scope|

    J'ai bien ajouté #include <algorithm> au début. Tu aurais une idée ?

  4. #4
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    std::all_of n'est dispo qu'avec C++11, si ton compilo le supporte active le.

    Sinon std::all_of peux s'implémenter simplement (code pompé sur la page de la doc)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class InputIterator, class UnaryPredicate>
      bool all_of (InputIterator first, InputIterator last, UnaryPredicate pred)
    {
      while (first!=last) {
        if (!pred(*first)) return false;
        ++first;
      }
      return true;
    }
    (probablement présent dans boost aussi, m'enfin il est dommage de créer une dépendance à boost juste pour ça)

  5. #5
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut
    Merci!

    Question un peu hors sujet: a ton avis, comment je pourrais faire pour proceder au comptage, j'ai réussi à faire un truc basique mais ça ne respecte pas les regles suivantes:

    Règle 1: On arrange ensuite les symboles dans l'ordre décroissant pour former un nombre qui sera lu en additionnant chacune des symboles.

    Par exemple 26 peut s'écrire comme XXVI ou XXIIIIII ou encore XVVVI. La première version étant préférée car utilisant moins de symboles.

    Règle 2: On peut utilise des soustractions en plaçant un (et uniquement un) symbole de petite valeur juste avant un symbole de plus grande valeur. Le résultat de cette manipulation est alors la différence entre le symbole le plus grand et le plus petit.

    Exemple: IV = 5-1 = 4 ou bien CIX = 100 + 10 - 1 = 109

    Règle 3: La soustraction ne peut se faire qu'avec les symboles suivants:
    I devant V ou X
    X devant L ou C
    C devant D ou M


    Ceci implique que 49 n'est pas IL mais XLIX

    Règle 4: Il n'y a pas de nombres plus grands que 4999 dans ce système, ni de nombres plus petits que 1.

    Règle 5: On utilise toujours la combinaison de symboles la plus courte.

    Donc XXVI au lieu de XVVVI, mais XLIX au lieu de IL sinon on viole la règle 3.
    en gros si tu tape IXXXCDL ca te donnera 681 (alors que la base est fausse).

    Je suis assez largué, c'est censé regardé si X n'a pas de I qui le précede, et pareil pour les autres, verifier que tout est bien dans l'ordre croissant... Je ne vois pas comment faire, des petits conseils ? ^^

    Merci encore.

    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    int ToArabian(string c)
    {
        int resultat(0);
        for(int i(0); i < sizeof(c); i++)
        {
            if(c[i] == 'I' && c[i+1] != 'V' && c[i+1] != 'X')
            {
                resultat += 1;
            }
            if(c[i] == 'V' && c[i-1] != 'I')
            {
                resultat += 5;
            }
            if(c[i] == 'X' && c[i-1] != 'I' && c[i+1] != 'L')
            {
                resultat += 10;
            }
            if(c[i] == 'L')
            {
                resultat += 50;
            }
            if(c[i] == 'C')
            {
                resultat += 100;
            }
            if(c[i] == 'D')
            {
                resultat += 500;
            }
            if(c[i] == 'M')
            {
                resultat += 1000;
            }
     
        }
        return resultat;
    }

  6. #6
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2011
    Messages
    27
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2011
    Messages : 27
    Points : 34
    Points
    34
    Par défaut
    Tu peux utiliser la fonction find() de string pour faire ça voici une partie de code présent dans le doc de C++ et un petit peu modifié pour ton code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    std::string str ("IXXXCDL");
    std::string str1 ("X");
    std::string str2 ("I");
     
    std::size_t foundX = str.find(str1);
    std::size_t foundI = str.find(str2);
    if (foundX>foundX)
        //fait quelquechose
    else
        //fait d'autre chose
    voici d'autres fonctions de la classe string qui pourront t'être utile

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    rfind();
    find_last_of();
    find_first_of()
    Le tout disponible avec doc ici

    http://www.cplusplus.com/reference/string/string/

  7. #7
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Je procéderais en 2 étapes :
    1 - vérifier que le nombre est valide
    2 - le traduire (ce qui est plus simple quand on a pas à vérifier la validité)

    En partant des règles on peut en déduire des contraintes à vérifier pour vérifier la validité du nombre.
    Règle 1 & 5 : (valide pour 1 / 10 / 100 / 1000)
    1 = I
    2 = II
    3 = III
    4 = IV
    5 = V
    6 = VI
    7 = VII
    8 = VIII
    9 = VIV
    10 = X
    Un symbole valant 1 (I, X, etc ...) ne peut pas être présent plus de 3 fois d'affilé.
    Un symbole valant 5 (V, L, etc...) ne peut pas être présent plus de 1 fois d'affilé. 2 avec une soustraction.

    Le respect de ça et la règle 3 implique le respect de la règle 4 (qui est donc inutile).

    Pour les règles 1 & 2, il faut vérifier que la soustraction soit autorisé.
    Au final, sur un exemple ça donne pour "XIXX"
    -> X : 1 sur 3 X
    -> I : caractère différent, si plus petit autorisé, sinon tester soustraction
    -> IX : soustraction autorisée, 2 sur 3 X, mais c'est le dernier autorisé à cause de la soustraction
    -> X : invalide, on viole la règle 1

    Une fois que tu sais que ton nombre respecte ça, tu n'as plus qu'a le traduire.
    Et tu peux te permettre pas mal de simplification car le nombre respecte des règles précises.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int last = 0, total = 0;
    for(int i=0; i<str.size(); ++i) {
        char c = str[i];
        int val = valueOf(c); // une fonction, une map, au choix,
                              // mais il faut un moyen de retrouver
                              // la valeur à partir d'un caractere ('I' = 1, 'V' = 5, etc..)
        if(last != 0 && val > last) { // si le nombre est plus grand que le précédent (-> 2eme nombre d'une soustraction)
            total-= 2 * last; // on avait ajouté last à l'itération précédente, on le soustrait 2 fois du coup
                              // à l'itération précédente on ne savait pas que c'était une soustraction
        }
        total += val;
        last = val;
    }
    Un petit détail aussi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::string c;
    for(int i(0); i < sizeof(c); i++) // attention à ça
    // sizeof(c) != c.size();
    // et seule c.size() te donne le nombre de caractères contenu dans le string

  8. #8
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    Un symbole valant 1 (I, X, etc ...) ne peut pas être présent plus de 3 fois d'affilé.
    Un symbole valant 5 (V, L, etc...) ne peut pas être présent plus de 1 fois d'affilé. 2 avec une soustraction.
    Bien vu ! Et comme tu le dis ça force à écrire le plus raccourci possible.

    Citation Envoyé par Iradrille Voir le message
    il faut vérifier que la soustraction soit autorisé.
    Comment faire niveau code ?

    Citation Envoyé par Iradrille Voir le message
    Au final, sur un exemple ça donne pour "XIXX"
    -> X : 1 sur 3 X
    -> I : caractère différent, si plus petit autorisé, sinon tester soustraction
    -> IX : soustraction autorisée, 2 sur 3 X, mais c'est le dernier autorisé à cause de la soustraction
    -> X : invalide, on viole la règle 1
    Je suis pas sûr d'avoir compris, tu veux dire:
    -> X : 10
    -> I : Autorisé si la lettre d'après est V ou X.
    -> X : C'est un X donc I est autorisé = 9
    -> X : IX(9) est placé avant, c'est donc invalide. 29 s'écrirait: XXIV et non XIXX

    Citation Envoyé par Iradrille Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int last = 0, total = 0;
    for(int i=0; i<str.size(); ++i) {
        char c = str[i];
        int val = valueOf(c); // une fonction, une map, au choix,
                              // mais il faut un moyen de retrouver
                              // la valeur à partir d'un caractere ('I' = 1, 'V' = 5, etc..)
        if(last != 0 && val > last) { // si le nombre est plus grand que le précédent (-> 2eme nombre d'une soustraction)
            total-= 2 * last; // on avait ajouté last à l'itération précédente, on le soustrait 2 fois du coup
                              // à l'itération précédente on ne savait pas que c'était une soustraction
        }
        total += val;
        last = val;
    }
    ValueOf(char c) c'est bien une fonction que je dois créer ? Du genre si c = 'I' return 1 ? Qu'est ce qu'une map ?
    Tu dis si last est différent de 0, mais last est initialisé à zéro et rien ne le modifie, j'ai du mal à comprendre le code.

    Citation Envoyé par Iradrille Voir le message
    Un petit détail aussi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::string c;
    for(int i(0); i < sizeof(c); i++) // attention à ça
    // sizeof(c) != c.size();
    // et seule c.size() te donne le nombre de caractères contenu dans le string
    Bon à savoir ça, merci !


    En tout cas merci pour ton implication, même si je ne comprends pas certains éléments ^^. Pour le comptage j'avais pensé à un truc dans ce genre, ex:

    439
    CDXXXI

    CM: 0
    CD: 1 (400)

    XC: 0
    XL: 0

    IX: 1 (9)
    IV: 0

    on retire le(s) chiffre(s) obtenu(s)


    Il reste XXX = 30

    on additionne: 400 + 9 + 30 = 439 = CDXXXI

    ---------

    Si je fais ça, je dois juste chercher les occurences des soustractions, une fois trouvé je les supprime du string, et ensuite je compte bêtement le reste sans prendre compte des soustractions (avec ValueOf par exemple)

  9. #9
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par Garwan50 Voir le message
    Comment faire niveau code ?
    Itérer sur tous les caractères (un for basique), à chaque caractère, vérifier s'il n'est pas répété trop de fois.
    Puis, si le caractère est supérieur au précédent, on vérifie que le "couple" de caractère qui défini la soustraction soit autorisé (plusieurs possibilités, des if/else ou une recherche dans un std::set<std::string> ou une recherche dans un std::set<std::pair<char, char>>).

    Citation Envoyé par Garwan50 Voir le message
    Je suis pas sûr d'avoir compris, tu veux dire:
    -> X : 10
    -> I : Autorisé si la lettre d'après est V ou X.
    -> X : C'est un X donc I est autorisé = 9
    -> X : IX(9) est placé avant, c'est donc invalide. 29 s'écrirait: XXIV et non XIXX
    Exactement.

    Citation Envoyé par Garwan50 Voir le message
    ValueOf(char c) c'est bien une fonction que je dois créer ? Du genre si c = 'I' return 1 ? Qu'est ce qu'une map ?
    Oui une fonction que tu dois créer.
    Une std::map est un tableau associatif
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::map<char, int> values;
    values.insert(std::make_pair('I', 1));
    values.insert(std::make_pair('V', 5));
     
    char c = 'I';
    int val = values[c]; // == 1
    Citation Envoyé par Garwan50 Voir le message
    Tu dis si last est différent de 0, mais last est initialisé à zéro et rien ne le modifie, j'ai du mal à comprendre le code.
    La condition pourrait être réécrite
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(i > 0 && val > last) {
    J'initialise last à 0, puis il prend la valeur du dernier caractère traité (qui est forcément != 0).
    Le but est ici que la condition soit fausse lors de la première itération (car last n'est pas correctement défini : il n'a pas la valeur du précédent caractère traité.

    Citation Envoyé par Garwan50 Voir le message
    Si je fais ça, je dois juste chercher les occurences des soustractions, une fois trouvé je les supprime du string, et ensuite je compte bêtement le reste sans prendre compte des soustractions (avec ValueOf par exemple)
    C'est une autre solution tout aussi valable.
    Utilise celle que tu trouves le plus logique et naturelle.

  10. #10
    Membre confirmé
    Profil pro
    Consultant en technologies
    Inscrit en
    Octobre 2013
    Messages
    158
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en technologies

    Informations forums :
    Inscription : Octobre 2013
    Messages : 158
    Points : 555
    Points
    555
    Par défaut
    Un recruteur m'a fait faire l'exercice inverse (D'arabe vers Romain) ca se fait en moins de 100 lignes (de C++, includes inclus), dans l'autre sens ca doit pas être plus compliqué

    Voilà 2-3 astuces
    * Il y a deux familles de caractères les 1 et les 5
    * On ne supprime que des 1 (Par exemple 1950 MCML)
    * On met maximum une unité à gauche (1883 =MDXXXLXXXIII et non MCCMXXCIIV)
    * l'unité de gauche est du meme ordre que le 5 (i.e. IV) ou de l'ordre inférieur à une unité (IX), par exemple 1999 != MIM

    à partir de là réfléchis un peu sur papier, et l'algo doit sortir assez naturellement.
    Une fois que tu as l'algo ca se code très facilement, si tu es capable d'écrire une boucle et un test conditionnel tu devrais pouvoir résoudre l'exercice.
    tu trouvera la doc de std::string par là :
    http://www.cplusplus.com/reference/string/string/

  11. #11
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par _zzyx_ Voir le message
    Un recruteur m'a fait faire l'exercice inverse (D'arabe vers Romain) ca se fait en moins de 100 lignes (de C++, includes inclus), dans l'autre sens ca doit pas être plus compliqué
    Le sens arabe -> romain est plus simple car il n'y à pas à vérifier l'entrée : un nombre est forcément valide (on peut éventuellement vérifier qu'il soit positif et pas trop grand).
    Après une simple table de correspondance donne un résultat direct.
    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
    16
    17
    18
    19
    20
    21
    const std::string values[][10] = {
    	{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"},
    	{"", "X", "XX", "XXX", "IX", "L", "LX", "LXX", "LXXX", "XC"},
    	{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
    	{"", "M", "MM", "MMM", "", "", "", "", "", ""}
    };
     
    unsigned int nb;
    std::cin >> nb;
    nb %= 3001; // nb doit pas etre trop grand
    std::string result = "";
    unsigned int div = 1000;
    unsigned int logdiv = 3;
    while(div) {
    	result += values[logdiv][nb / div];
    	nb %= div;
    	div /= 10;
    	--logdiv;
    }
     
    std::cout << result << std::endl;
    Dans le sens romain -> arabe, la traduction est simple quand le nombre est valide, mais cette validation est la parti la plus dure de l'exo.

    Bien qu'en y repensant une petite regex et la validation est faite... Peut être un peu overkill pour ça, mais ça à le mérite d'être une solution simple à mettre en place.

    edit : @_zzyx_, en fait tu as raison, dans un sens ou dans l'autre c'est exactement le même problème.
    Utiliser un tableau de correspondance marche dans les deux cas et le code est proche.
    Dans le sens arabe -> romain, j'ai immédiatement pensé à ça, dans l'autre sens, je pense que je n'y aurais jamais pensé. :/

  12. #12
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    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
    16
    17
    18
    19
    20
    21
    const std::string values[][10] = {
    	{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"},
    	{"", "X", "XX", "XXX", "IX", "L", "LX", "LXX", "LXXX", "XC"},
    	{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
    	{"", "M", "MM", "MMM", "", "", "", "", "", ""}
    };
     
    unsigned int nb;
    std::cin >> nb;
    nb %= 3001; // nb doit pas etre trop grand
    std::string result = "";
    unsigned int div = 1000;
    unsigned int logdiv = 3;
    while(div) {
    	result += values[logdiv][nb / div];
    	nb %= div;
    	div /= 10;
    	--logdiv;
    }
     
    std::cout << result << std::endl;
    Un code, ou plutot de l'algo un peu compliqué pour moi.
    En tout cas je vais essayer de réussir le programme grâce à votre aide.
    Car Iradrille j'aurais jamais pensé à ce que tu m'as proposé, merci encore !

    Je vous tiens au courant !

  13. #13
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut
    Ca marche presque, sauf que la regle 1, 3 et par consequente la regle 5 ne sont pas respectés.

    En gros le comptage se fait bien.
    Mais il ne vérifie pas que le chiffre est décroissant. (puisque il ne tient pas compte de la regle 2.

    Il ne vérifie pas si le chiffre pour la soustraction est bon (IM fonctionne donc.).

    Il ne calcule pas si c'est la combinaison la plus simple (ça je ne sais pas comment faire).



    Je pourrais modifier ce code:
    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
    16
    17
    int ToArabian(string s)
    {
        int resultat(0);
        int last(0);
        for(int i = 0; i < s.size(); i++)
        {
            char c = s[i];
            int val = ValueOf(c);
            if(last != 0 && val > last) // Il vérifie juste que le précedent est supérieur à l'actuel
            {
                resultat -= (2*last);
            }
            resultat += val;
            last = val;
        }
        return resultat;
    }
    mais je vais devoir faire 6 if pour vérifier si c'est IV, IX, XL, XC, CD, CM.

    Vous auriez des derniers conseils pour améliorer ça ??

  14. #14
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par Garwan50 Voir le message
    mais je vais devoir faire 6 if pour vérifier si c'est IV, IX, XL, XC, CD, CM.

    Vous auriez des derniers conseils pour améliorer ça ??
    C'est ce que j'essayais d'expliquer, faire ça en 2 étapes, une vérification et une traduction.

    Pour la vérification, l'algo général est :
    Citation Envoyé par Iradrille Voir le message
    Itérer sur tous les caractères (un for basique), à chaque caractère, vérifier s'il n'est pas répété trop de fois.
    Puis, si le caractère est supérieur au précédent, on vérifie que le "couple" de caractère qui défini la soustraction soit autorisé (plusieurs possibilités, des if/else ou une recherche dans un std::set<std::string> ou une recherche dans un std::set<std::pair<char, char>>).
    Mais clairement utiliser une table donnant les correspondances est le plus simple à mettre en place (cf mon dernier code).
    Il faut juste inverser la table mais le fonctionnement est identique.

  15. #15
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut
    Salut et merci une fois de plus pour ta réponse.

    Le problème, c'est que je ne comprend pas ce code:
    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
    16
    17
    18
    19
    20
    21
    const std::string values[][10] = {
    	{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"},
    	{"", "X", "XX", "XXX", "IX", "L", "LX", "LXX", "LXXX", "XC"},
    	{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
    	{"", "M", "MM", "MMM", "", "", "", "", "", ""}
    };
     
    unsigned int nb;
    std::cin >> nb;
    nb %= 3001; // nb doit pas etre trop grand
    std::string result = "";
    unsigned int div = 1000;
    unsigned int logdiv = 3;
    while(div) {
    	result += values[logdiv][nb / div];
    	nb %= div;
    	div /= 10;
    	--logdiv;
    }
     
    std::cout << result << std::endl;
    Le tableau values, et l'algo etc.
    Je vais voir avec les autres méthode que tu m'as donné, mais le tableau de correspondance je ne comprends pas.


    EDIT: j'ai un petit soucis:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class InputIterator, class UnaryPredicate>
      bool all_of (InputIterator first, InputIterator last, UnaryPredicate pred)
    {
      while (first!=last) {
        if (!pred(*first)) return false; // ligne 13
        ++first;
      }
      return true;
    }
    C:\Users\PC-HP\Desktop\chiffre\romain\main.cpp|13|error: invalid conversion from 'char' to 'const char*' [-fpermissive]|

  16. #16
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    L'algo est en soit très simple, il peu paraître compliqué à cause des divisions et modulos qui, si non compris, donnent l'impression de sortir de nul part.

    On à un nombre "nb" en entrée (nombre à traduire en romain).
    On prend chaque chiffre de ce nombre un à un (pour 1883, on prend 1 puis 8, puis 8, puis 3).

    1883 = 1*10^3 + 8*10^2 + 8*10^1 + 3*10^0.

    En rouge la puissance, notée "logdiv" dans l'algo.
    "div" = 10*^logdiv. (D'où logdiv = log10(div)).

    Ceci dit, prenons l'algo ligne par ligne
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    nb %= 3001; // nb doit pas etre trop grand
    // la traduction n'est valide que pour des nombres petit, il est impossible
    // de traduire 1 456 833 par exemple, je pose ici une limite arbitraire à 3000
    // je prend le modulo de nb pour avoir un nombre >= 0 et <= 3000
     
    std::string result = "";
    // rien à dire ici
     
    unsigned int div = 1000;
    // le premier diviseur à tester, le nombre maximum qu'on peut traduire
    // est >= 1 000 et < 10 000, d'où div = 1 000
     
    unsigned int logdiv = 3;
    // == log10(div), (hardcodé se qui le rend probablement moins compréhensible)
     
    while(div) {
    // tant que div != 0
     
    	result += values[logdiv][nb / div];
    	// nb / div permet de récupérer le chiffre que l'on veut traduire
    	// (1, puis 8, puis 8, puis 3, pour l'exemple de 1883)
    	// pour 1883 toujours, à la première itération logdiv == 3, (nb / div) == 1
    	// on va donc chercher la valeur de values[3][1] (== "M")
    	// je te laisse dérouler les autres itérations.
    	// on ajoute la valeur récupéré (le "M") au résultat
     
    	nb %= div;
    	// pour nb == 1883, première itération (div == 1000), 1883 % 1000 == 883
    	// le but est de pouvoir passer au chiffre suivant en "supprimant" le "1"
    	// qu'on vient de traiter
     
    	div /= 10;
    	// nb est 10 fois plus petit (en ordre de grandeur), il faut ajuster
    	// le diviseur (nb est en base décimale)
    	// ce 10 n'a rien de magique, si nb était donné en binaire on aurait 2 etc..
     
    	--logdiv;
    	// logdiv == log10(div), encore une fois hardcodé ici, je me sers de 
    	// log10(10*n) == log10(n) + 1
     
    }
    Pour inverser la table de correspondance, il y à une astuce :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::string[4][10];
    // est ici utilisé comme un
    std::map<int, std::string>[4]
    On peut se permettre cette simplification car les nombres en entrées sont des int >= 0 et <= 10 qui se suivent, l'indice dans le tableau sert donc de clé pour retrouver la valeur.
    Mais d'un point de vue algo les 2 (tableaux et map) sont identiques (sauf du point de vue complexité et donc temps d'exécution, mais ce n'est pas important ici).

    Ce tableau contient des valeurs permettant la traduction :
    * la première dimension est la puissance (unité = 0, dizaine = 1, centaine = 2, millier = 3)
    * la deuxième dimension est le chiffre (correspondance exacte, d'où l'équivalence tableau <=> map)

    Ainsi, si tu veux traduire 1883 :
    pour traduire le 1 : puissance = 3 (millier), valeur = 1 => values[3][1] = "M"
    le 1er 8 : puissance = 2 (centaine), valeur = 8 => values[2][8] = "DCCC"
    le 2eme 8 : puissance = 1 (dizaine), valeur = 8 => values[1][8] = "LXXX"
    le 3 : puissance = 0 (unité), valeur = 3 => values[0][3] = "III"

    résultat = M DCCC LXXX III.
    En espérant que ce soit clair.

    Normalement tu dois voir apparaître la façon de faire l'inverse.


    Citation Envoyé par Garwan50 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    C:\Users\PC-HP\Desktop\chiffre\romain\main.cpp|13|error: invalid conversion from 'char' to 'const char*' [-fpermissive]|
    Aucune idée, avec les templates il faut donner plus d'infos (contexte dans lequel se fait l'instanciation, types instanciés etc..)
    Sans ça, il est difficile (impossible ?) de trouver le problème.
    Citation Envoyé par Iradrille Voir le message
    A propos de cette ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return std::find(tokens.begin(), tokens.end(), cc) != tokens.end();
    J'ai lu un peu partout qu'il était préférable d'utiliser std::begin(container) au lieu de container.begin(), car plus facilement remplaçable et donc plus générique.
    VS2012 n'aime pas, il est perdu.
    Est-ce normal (construction automatique d'un vector ou autre via un initializer-list par exemple), ou VS déconne ?
    J'en profite pour me répondre au cas où ça servirai à quelqu'un : l'implémentation de initializer_list dans VS2012 est partielle et les spécialisation de std::begin et std::end ne sont pas présentes.
    C'est donc un problème propre à VS2012.

  17. #17
    Nouveau membre du Club
    Profil pro
    Lycéen
    Inscrit en
    Novembre 2009
    Messages
    67
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Novembre 2009
    Messages : 67
    Points : 34
    Points
    34
    Par défaut
    J'ai beaucoup de mal avec ta méthode. Je ne suis pas adepte des tableaux de correspondances, des div etc. Pour êtres tout à fait honnête, je ne comprends pas du tout.

    J'avais favorisé la méthode que tu utilisais plus au dessus (pour le comptage), j'ai essayé en vain de l'adapter pour la vérification. Cependant, ça ne semble pas fonctionner.

    Toutes les solutions que j'imagine pour que le programme prenne en compte chacune des 5 règles me ramènent à ta méthode, c'est à dire compter bêtement le chiffre entré par l'utilisateur, voir ce que ça donnerait en chiffre romain, pour ensuite apporter la correction.

    Je pourrais simplement copier coller ta solution, je trouve ça dommage de bloquer sur de l'algo plutot que sur le code.

    La prochaine fois je suppose que je m'orienterais dans des exercices qui ne comprennent pas de l'algo.

    Je vous remercie pour l'aide que vous m'avez apporté.
    Bonne soirée.

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

Discussions similaires

  1. [XL-2010] "Out of memory" à la fin d'une routine manipulant des "string" importants
    Par langeard dans le forum Macros et VBA Excel
    Réponses: 11
    Dernier message: 21/05/2015, 10h20
  2. [c#] Comment manipuler des string ?
    Par Mickey.jet dans le forum C#
    Réponses: 4
    Dernier message: 27/05/2009, 08h14
  3. Réponses: 1
    Dernier message: 08/05/2008, 14h28
  4. Manipulation des String
    Par ALIAS200 dans le forum API standards et tierces
    Réponses: 11
    Dernier message: 09/05/2007, 19h46
  5. [VB]manipulation des caractères d'un string
    Par lacsap49 dans le forum VB.NET
    Réponses: 5
    Dernier message: 16/06/2006, 18h43

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