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++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    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
    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
    Membre Expert
    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
    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
    Membre confirmé
    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
    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
    Membre Expert
    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
    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
    Membre confirmé
    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
    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
    Membre actif
    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
    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
    Membre Expert
    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
    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
    Membre Expert
    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
    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.

  9. #9
    Membre confirmé
    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
    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