Partage
  • Partager sur Facebook
  • Partager sur Twitter

TP : La POO en pratique avec ZString

La méthode remplacer()

    13 août 2008 à 19:26:57

    L'un d'entre vous a-t'il essayé d'implémenter la méthode remplacer() de la classe "ZString" telle qu'elle est présentée dans le chapitre Aller (encore) plus loin du TP "La POO en pratique avec ZString" ?

    Citation : vue dans le chapitre du TP mentionné ci-dessus

    Dans le même style, on peut imaginer une méthode remplacer() qui prend au moins 2 paramètres : ce que vous recherchez, et par quoi vous voulez le remplacer.



    J'imaginais plutôt une méthode avec ce type de signature :

    void remplacer(const char *motifARemplacer, const char *nouveauMotif);
    


    Je l'ai implémentée mais j'ai du créer une méthode de concaténation de chaînes dans la classe "ZString" pour y arriver.

    Voici la signature de cette méthode de concaténation :

    void zStrcat(const char *chaine);
    


    Si vous avez une solution à partager ca m'intéresse !
    Bonne soirée à tous.

    Voici le code ma méthode remplacer() :

    void ZString::remplacer(const char *motifARemplacer, const char *nouveauMotif)
    {
        int i = 0; // indice de parcours de m_chaine
        int j; // indice de parcours du motif cherché
        int cpt_car; // compteur du nombre de caractères consécutifs égaux à ceux du motif cherché
        bool trouve; // booléen mis à "true" si un motif à été trouvé
    
        // Parcours de m_chaine
        while (i < m_longueur) {
            // Initialisations
            j = 0;
            cpt_car = 0;
            trouve = false;
            // Recherche du motif
            while (j < longueur(motifARemplacer)) {
                if (m_chaine[i+j]==motifARemplacer[j]) {
                    cpt_car++;
                }
                // Le premier caractère cherché est différent, inutile de chercher plus loin
                else {
                    j = longueur(motifARemplacer);
                }
                if (cpt_car == longueur(motifARemplacer)) {
                    trouve = true;
                }
                j++;
            }
            // Motif non trouvé, on copie le caractère courant de m_chaine
            if (!trouve) {
                resultat.zStrcat(m_chaine[i]);
                i++;
            }
            // Motif trouvé, on copie le nouveau motif et on continue le parcours de m_chaine après le motif retiré
            else {
                resultat.zStrcat(nouveauMotif);
                i = i + longueur(motifARemplacer);
            }
        }
        // réinitialisation de la ZString d'appel avec les nouvelles valeurs
        delete[] m_chaine;
        m_chaine = resultat.m_chaine;
        m_longueur = longueur(resultat.m_chaine);
    }
    
    • Partager sur Facebook
    • Partager sur Twitter
      13 août 2008 à 19:36:12

      Oulà... je sais que tu te compliques la vie assez intensément.
      Voici comment j'avais procéder :
      void MString::remplacer(char *chaineRecherche, char *chaineRemplace, int nbFois)
      {
          size_t taille = longueur(chaineRecherche);
          if (taille > longueur(chaineRemplace))
              taille = longueur(chaineRemplace);
          for (size_t i = 0; m_mString[i] != '\0' && nbFois != 0; i++)
          {
              size_t j;
              for (j = 0; m_mString[i+j] == chaineRecherche[j] && j < taille; j++);
              if (j == taille)
              {
                  for (j = 0; j < taille; j++)
                      m_mString[i+j] = chaineRemplace[j];
                  nbFois--;
              }
          }
      }
      


      En gros, au début, je prend la taille de la chaine la plus courte entre mes deux arguments.
      Ensuite, une première boucle pour couvrir toute la chaine (nbFois sert à permettre de remplacer plus d'un caractère, par exemple pour remplacer les deux premiers 'e' dans premierement)
      La deuxième boucle s'exécute si on trouve le premier caractère de la chaineRecherche et continue tant que tous les caractères n'ont pas été vérifiés.
      Finalement, si on arrive au dernier caractère de la chaineRecherche (ou alors que chaineRemplac est plus courte que chaineRecherche et qu'on est au dernière caractère de chaineRemplace), on remplace chaque caractère.


      Je te laisse analyser le code. (MString, C'est en fait la classe ZString et m_mString, c'est m_chaine)

      Bonne continuation.
      • Partager sur Facebook
      • Partager sur Twitter
        13 août 2008 à 20:58:42

        void void MString::remplacer(char *chaineRecherche, char *chaineRemplace, int nbFois)

        Il y a deux void ;)
        • Partager sur Facebook
        • Partager sur Twitter
          13 août 2008 à 21:01:38

          Citation : Hertzien'

          void void MString::remplacer(char *chaineRecherche, char *chaineRemplace, int nbFois)

          Il y a deux void ;)



          Corriger. Erreur de copié/collé.
          • Partager sur Facebook
          • Partager sur Twitter
            13 août 2008 à 22:11:00

            Merci Mikechaos pour ta réponse. Au passage, c'est bien vu d'avoir mis le parametre nbFois.
            J'ai analysé ton code puis testé avec 3 cas sur la chaine "toto titi toto titi toto".

            1er cas : remplacer toutes les occurrences de "toto" par "tata".
            résultat         : "tata titi tata titi tata"

            2ème cas : remplacer toutes les occurrences de "to" par "tata".
            résultat         : "tata titi tata titi tata"
            résultat attendu : "tatatata titi tatatata titi tatatata"

            3ème cas : remplacer toutes les occurrences de "toto" par "ta".
            résultat         : "tata titi tata titi tata"
            résultat attendu : "ta titi ta titi ta"

            Si ton objectif était bien le même que le mien, je pense que ton erreur se trouve dans la définition de ta variable "taille".

            En ce qui concerne mon code, on peut surement le réduire.
            Précision sur mon utilisation de "zStrcat()". Sans cette méthode, je n'arrivais pas à concaténer correctement avec les opérateurs classiques et surchargés. J'obtenais des dégâts niveau mémoire qui se traduisaient par l'affichage de caractères cabalistiques à la fin.
            Bonne continuation !

            • Partager sur Facebook
            • Partager sur Twitter
              13 août 2008 à 22:23:08

              Eum.. je ne sais pas comment tu as testé, mais le code suivant me renvoi le bon résutalt :

              #include <iostream>
              #include "MString.h"
              using namespace std;
              
              int main(void)
              {
                  MString chaine = "tata titi tata titi tata";
                  chaine.remplacer("tata", "toto");
                  cout << endl << chaine << endl;
                  return 0;
              }
              


              Résultat :
              toto titi toto titi toto
              
              Process returned 0 (0x0)   execution time : 0.015 s
              Press any key to continue.


              J'ai oublié de préciser aussi que nbFois est un paramètre facultatif initialisé à -1.
              si l'argument n'est pas précisé, remplacer va donc changer toutes les occurrences de l'arg1 par l'arg2 dans la chaine.
              • Partager sur Facebook
              • Partager sur Twitter
                13 août 2008 à 22:37:06

                Le résultat que tu affiches correspond à mon 1er cas de test. Comme le montre ton post, ce résultat est conforme à ce qu'on attend.

                Teste à présent mes 2ème et 3ème cas en ajoutant ces lignes :

                chaine = "tata titi tata titi tata";
                chaine.remplacer("ta", "toto");
                chaine = "tata titi tata titi tata";
                chaine.remplacer("tata", "to");
                


                Encore une fois, bien vu pour "nbFois" en facultatif :)
                • Partager sur Facebook
                • Partager sur Twitter
                  13 août 2008 à 23:16:12

                  Ah! Je viens de comprendre. En fait, c'est bien le comportement attendu. Dans ma tête, il fallait que les deux chaines est la même taille.

                  C'était donc voulu. Mais je comprends pourquoi ton algorithme est si long!

                  Par contre, zStrcat est équivalente à l'opérateur += (const char*), je ne vois donc pas pourquoi tu en de besoin.

                  Ton problème ce situe forcément au niveau de cet opérateur. Serait-il possible de le voir?
                  • Partager sur Facebook
                  • Partager sur Twitter
                    13 août 2008 à 23:27:30

                    Voici le code de "zStrcat()". Pour information, je l'ai surchargée pour accepter en paramètre un caractère ou encore une ZString. Dans cet exemple, tu as le cas avec en paramètre une chaine de caractères.

                    Cette méthode est écrite en détaillée (certaines affectations peuvent être simplifiées).
                    Pourquoi ne pas avoir surchargé l'opérateur "+=" ? J'aurai pu le faire, mais j'ai déjà surchargé les opérateurs '+' et '='.

                    J'ai utilisé "zStrcat()" parce qu'avec mes opérateurs surchargés '+' et '=' je n'arrivais pas à concaténer sans obtenir des erreurs mémoires (cf post précédent) à l'exécution.

                    Si tu arrives au même résultat en utilisant ces opérateurs ou un '+=' surchargé à la place de "zStrcat()" je suis preneur.

                    Code de la méthode "zStrCat() de la classe ZString :
                    char *ZString::zStrcat(const char *chaine)
                    {
                        if (m_chaine == NULL) {
                            m_chaine = new char[1];
                            m_chaine[0] = '\0';
                        }
                    
                        int tailleChaineSource = m_longueur;
                        int tailleChaineCible = longueur(chaine);
                        int tailleChaineCat = tailleChaineCible + tailleChaineSource;
                    
                        int i = 0 ;
                    
                        char *chaineCat = new char[tailleChaineCat + 1]; // +1 pour stocker \0
                    
                        for (i = 0 ; i < tailleChaineSource ; i++)
                        {
                            chaineCat[i] = m_chaine[i];
                        }
                        for (int j = 0 ; j < tailleChaineCible ; j++)
                        {
                            chaineCat[i+j] = chaine[j];
                        }
                        chaineCat[tailleChaineCat] = '\0';
                        delete[] m_chaine;
                        m_chaine = chaineCat;
                        m_longueur = longueur(chaineCat);
                    
                        return m_chaine;
                    }
                    

                    Signatures de mes opérateurs surchargés :
                    ZString operator=(const char *chaine);
                    ZString operator=(const ZString &chaine);
                    ZString operator+(const char caractere);
                    ZString operator+(const char *chaine);
                    ZString operator+(const ZString &chaine);
                    


                    Bonne continuation !
                    • Partager sur Facebook
                    • Partager sur Twitter
                      14 août 2008 à 2:10:48

                      Alors voilà une méthode qui devrait répondre à tes besoins :
                      (Note, il faut utiliser la surcharge de l'opérateur +=(char))
                      void MString::remplacer(char *chaineRecherche, char *chaineRemplace, int nbFois)
                      {
                          /*On declare deux variable, chacune aillant pour valeur, la taille d'une
                          des deux chaines*/
                          size_t tailleRecherche = longueur(chaineRecherche),
                          tailleRemplace = longueur(chaineRemplace);
                      
                          MString chaine; // l'objet sur lequel nous allons travailler.
                          /*Faire attention par contre, il faut que ton constructeur par defaut
                          initialise ta chaine a 0 et non a NULL.
                          L'appel du constructeur par defaut donc doit etre equivalent a 
                          MString chaine(0)*/
                          
                          for (size_t i = 0; i < m_longueur; i++)
                          {
                              for (size_t j = 0; m_mString[i+j] == chaineRecherche[j] and j < tailleRecherche and nbFois != 0; j++)
                              {
                                  if (j+1 == tailleRecherche)
                                  {
                                      /*Pour eviter la dupplication des caracteres avec la ligne qui
                                      suit la boucle for, on ajoute un nombre de caractere suffisant
                                      pour couvrir toute la partie qui sera remplacee*/
                                      i += tailleRecherche;
                                      for (j = 0; j < tailleRemplace; j++)
                                          chaine += chaineRemplace[j];
                                      
                                      nbFois--;
                                      /*On remis j a 0 au cas ou les caracteres suivant dans
                                      m_mString serait aussi a remplacer*/
                                      j = 0;
                                  }
                              }
                              //On peut ensuite ajouter les caracteres qui ne seront pas remplace
                              chaine+= m_mString[i];
                      
                          }
                          *this = chaine;
                      }
                      


                      Et le code pour la surcharge de l'opérateur += :
                      void MString::operator+=(char c)
                      {
                          char chaine[m_longueur+2];
                          for (size_t i = 0; i < m_longueur; i++)
                              chaine[i] = m_mString[i];
                              
                          chaine[m_longueur] = c;
                          chaine[m_longueur + 1] = 0;
                          this->vider();
                          m_mString = copie(chaine);
                          m_longueur = longueur(chaine);
                      }
                      


                      la méthode vider() ne fait que remettre le chaine à 0 (bien à 0, et non à NULL) et remet aussi m_longueur à zéro.
                      La méthode copie est dans le cours de M@téo, mais je n'ai pas vraiment suivit le tuto pour faire cette classe donc je la mets quand même :
                      char* MString::copie(const char *chaine)
                      {
                          size_t taille = longueur(chaine);
                      
                          char *chaineCopie = new char[taille + 1];
                      
                      
                          for (size_t i = 0 ; i < taille ; i++)
                              chaineCopie[i] = chaine[i];
                          chaineCopie[taille] = '\0';
                      
                          return chaineCopie;
                      }
                      


                      Voilà. Si tu peux réussir à comprendre ma logique, je crois que tu devrais être capable de la reproduire dans ton code.
                      Le mieux serait bien sûr que tu comprennes, et non que tu recopies bêtement toutes ces méthodes.
                      • Partager sur Facebook
                      • Partager sur Twitter
                        14 août 2008 à 10:43:26

                        Merci pour tes indications. Je pense que l'origine de mes problèmes de mémoire venait de mes initialisations de chaine à NULL. J'ai changé ma méthode "vider()" et mon constructeur par défaut pour initialiser mon attribut "m_chaine" à '\0'.
                        Pourquoi fais tu tes initialisations avec des '0' ? Faisais-tu référence au '\0' ?

                        Pour le reste, j'ai implémenté les surcharges de l'opérateur "+=" pour accepter un "const char" et un "const char*". Je vais essayer de modifier mes surcharges de '+' et '=' pour faire une version de "remplacer()" qui les utilisera. Ca devrait passer tout seul.

                        Une remarque sur la sortie de ton opérateur surchargé "+=" :
                        this->vider();
                        m_mString = copie(chaine);
                        m_longueur = longueur(chaine);
                        

                        Ta méthode "vider()" ne fais que réaffecter ton attribut "m_mString" à '\0'. L'ancien contenu est perdu dans la mémoire. un "delete[] m_mString;" me semble plus approprié.
                        De même en fin de traitement, je rajouterais une ligne "delete[] chaine;" comme tu n'as plus besoin d'utiliser la variable "chaine". Tu obtiendrais alors ce code :
                        delete[] m_mString;
                        m_mString = copie(chaine);
                        m_longueur = longueur(chaine);
                        delete[] chaine;
                        

                        J'ai utilisé ces dernières lignes pour sortir de ma méthode "+=" et ça fonctionne.
                        Bonne continuation !
                        • Partager sur Facebook
                        • Partager sur Twitter
                          14 août 2008 à 14:52:24

                          Citation : Toi

                          Pourquoi fais tu tes initialisations avec des '0' ? Faisais-tu référence au '\0' ?

                          Parce que le caractère '\0' est en fait le nombre 0. '\0' == 0

                          Citation : Ga

                          De même en fin de traitement, je rajouterais une ligne "delete[] chaine;" comme tu n'as plus besoin d'utiliser la variable "chaine". Tu obtiendrais alors ce code :


                          En effet, le deuxième delete[], je l'ai supprimé par erreur (sans m'en rendre compte). Merci de me l'avoir signalé.
                          Par contre, ma méthode vider() fait elle-même le premier delete[] :
                          void MString::vider(void)
                          {
                              delete[] m_mString;
                              m_mString = new char;
                              m_mString = 0;
                              m_longueur = 0;
                          }
                          


                          Bonne continuation.
                          • Partager sur Facebook
                          • Partager sur Twitter
                            14 août 2008 à 16:04:40

                            OK pour la méthode "vider()" et les "delete[]".

                            Par contre, je ne savais pas qu'un <char *str = '\0';> était équivalent à <char *str = 0;>. Merci pour l'info.

                            Bonne continuation !
                            • Partager sur Facebook
                            • Partager sur Twitter

                            TP : La POO en pratique avec ZString

                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                            • Editeur
                            • Markdown