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

Télécharger C Discussion :

Remplacer toutes les occurrences d'une sous-chaîne


Sujet :

Télécharger C

  1. #1
    Membre émérite
    Remplacer toutes les occurrences d'une sous-chaîne
    Bonjour, Je vous propose un nouvel élément à utiliser : Remplacer toutes les occurrences d'une sous-chaîne



    Remplace toutes les occurrences de Avant par Apres dans la chaine txt, puis renvoie un pointeur sur la nouvelle chaîne créée. Renvoie NULL si txt ne contient aucune occurrence de Avant.



    Qu'en pensez-vous ?

  2. #2
    Nouveau Candidat au Club
    Test retour malloc
    Bonjour,
    Très pratique cette fonction ! merci.
    Cependant attention les valeurs de retour des allocations ne sont pas testés, elles sont suivis par des copies -> segfault.
    Bonne journée

  3. #3
    Expert confirmé
    Bonjour,

    Ton code actuel réussit bien les tests suivants :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    char* res1 = str_replace("O_O_O_O_O_O", "O_O", "XY");
    assert(0 == strcmp(res1, "XY_XY_XY"));
     
    char* res2 = str_replace("O_O_O_O_O_O", "O_O", "");
    assert(0 == strcmp(res2, "__"));


    Dans la suite de mon message, je vais noter N le nombre d'occurrences de Avant dans txt.

    Je propose les améliorations suivantes :
    • Si N est non nul, Ton code fait un appel à malloc et N+1 appels à realloc. Il serait plus performant de calculer N puis de faire une seule allocation :
      Code :Sélectionner tout -Visualiser dans une fenêtre à part
      TxtRetour = (char *) malloc(strlen(txt) - N*strlen(Avant) + N*strlen(Apres) + 1);
    • Si une allocation échoue, la fonction devrait retourner NULL.
    • Il y a un commentaire à corriger :
      Code :Sélectionner tout -Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      /* Aucune occurrences : renvoie simplement une copie de la chaine */
      if (pos == NULL)
      {
          return NULL;
      }

      En effet, ce code ne renvoie pas une copie de la chaîne : il renvoie NULL.
    • Il y a un bout de ton code qui est de la forme :
      Code :Sélectionner tout -Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      BOUT_DE_CODE_B;
      while(condition)
      {
          BOUT_DE_CODE_A;
          BOUT_DE_CODE_B;
      }

      Dans ton cas, BOUT_DE_CODE_B est :
      Code :Sélectionner tout -Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      /* Ajoute la chaîne de remplacement */
      Long = strlen (Apres);
      strncpy (TxtRetour + PosTxtRetour, Apres, Long);
      PosTxtRetour += Long;
       
      /* Cherche la prochaine occurrence */
      pos = strstr (txt, Avant);

      Ce code peut être factorisé, par exemple de cette manière :
      Code :Sélectionner tout -Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      while(true)
      {
          BOUT_DE_CODE_B;
          if(!codition)
              break;
          BOUT_DE_CODE_A;
      }
    • Au début de ta fonction, je te conseille d'ajouter :
      Code :Sélectionner tout -Visualiser dans une fenêtre à part
      1
      2
      3
      assert(strlen(Avant) > 0);
      if(strlen(Avant) == 0)
          return NULL;

      Sinon, l'utilisateur risque d'être bloqué dans une boucle infinie.
    • Dans ton premier appel à realloc, tu as bien pensé à convertir le résultat en (char *). Il faudrait aussi le faire pour ton appel à malloc et ton autre appel à realloc. Ainsi, le code de ta fonction compilera aussi en C++.
    • Pour aller plus loin, tu pourrais factoriser le code en fournissant une nouvelle fonction str_replace_without_alloc utile aux utilisateurs qui voudraient gérer la mémoire autrement :
      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
      // nombre d'occurrences de substring dans txt
      // retourner -1 si substring est une chaîne vide
      int str_count(const char *txt, const char *substring)
      {
          // ...
      }
       
      // remplacer toutes les occurrences de Avant par Apres dans la chaine txt
      // retourner false en cas d'erreur
      bool str_replace_without_alloc(char *Resultat, size_t tailleMaxResultat, const char *txt, const char *Avant, const char *Apres)
      {
          assert(Resultat != txt);
          // ...
      }
       
      char *str_replace(const char *txt, const char *Avant, const char *Apres)
      {
          char* TxtRetour = NULL;
       
          assert(strlen(Avant) > 0);
          if(strlen(Avant) == 0)
              return TxtRetour;
       
          const int N = str_count(txt, Avant);
          if(N <= 0)
              return TxtRetour;
       
          const size_t tailleTxtRetour = strlen(txt) - N*strlen(Avant) + N*strlen(Apres);
          TxtRetour = (char *) malloc(tailleTxtRetour + 1);
          if(TxtRetour == NULL)
              return TxtRetour;
       
          if(!str_replace_without_alloc(TxtRetour, tailleTxtRetour, txt, Avant, Apres)) {
              free(TxtRetour);
              TxtRetour = NULL;
          }
       
          return TxtRetour;
      }