Aide - Programme C++

Résolu
May -  
 May -
Bonjour! Je suis débutante en C++ et j'ai de la misère à faire un exercice de programmation (je ne comprends pas pourquoi ça ne fonctionne pas, même si mon compilateur ne me renvoie aucune erreur). Voici ce que dit l'exercice :
Écrire une fonction dont le prototype est int incluse (char chaine1[] , char chaine2[]) ; . La fonction incluse() retourne le nombre d'occurence de chaine1 dans chaine2. La fonction incluse peut être définie par plusieurs fonctions.
Exemple: chaine1 = 'Bonjour'
chaine2 = 'jour'
résultat = 1

Voici ce que j'ai fait :
#include <iostream>
#include <string.h>

using std::cout ;
using std::cin ;
using std::endl ;

int incluse (char chaine1[] , char chaine2[]) {

int compteur = 0 ;
int i ;
int j = 0 ;

for (i=0 ; i<strlen(chaine2) ; i++) {
while (j<strlen(chaine1)) {
if (chaine1[j] == chaine2[i]) {
j++ ;
}}
if (j==strlen(chaine1)) {
compteur++;
}
}
return compteur ;
}



main () {

char chaine1[100] ;
char chaine2[100] ;
int compteur ;

cout << "Entrer une chaine de caracteres : \n" ;
cin >> chaine2 ;

cout << "Voici la chaine de caracteres que vous avez entre : \n" << chaine2 <\
< endl ;

cout << "Entrer une sequence de caracteres a rechercher : \n" ;
cin >> chaine1 ;

compteur = incluse (chaine1 , chaine2) ;
cout << "Le nombre d'occurence de la 1re chaine dans la 2e est : \n" << compt\
eur << endl ;

return 0 ;
}



Aidez-moi je vous en prie! Merci beaucoup pour votre aide!

16 réponses

mamiemando Messages postés 33784 Date d'inscription   Statut Modérateur Dernière intervention   7 885
 
Tu peux déjà rajouter le int devant ton main :
int main(){
  ...
}


Actuellement tu utilises string.h qui est un header C. Je te propose d'utiliser plutôt les string du C++ qui sont nettement plus pratique :
//#include <string.h>
#include <string>

Les strings s'appèlent std::string (de même que les sorties et entrées standards sont std::cerr, std::cout, std::cin), donc si tu es dans un fichier .cpp, tu peux omettre le std:: en mettant :
using namespace std;

Le prototype de estIncluse devient :
int incluse (std::string chaine1 ,std::string chaine2){
  ...
}

L'intérêt des string est qu'elles proposent pas mal de fonctions sympatiques (recherche d'une sous-chaine, extraction d'un morceau de chaine, taille, opérateur d'égalité, d'affectation...)

Ensuite comme tu le sais sans doute, en C et en C++ les paramètres sont recopiés en mémoire. Dans ton cas ce sera les deux chaînes complètes qui seront donc recopiées ! Ceci est d'autant plus gênant que les structures passés en paramètres sont grandes. On utilise donc des pointeurs, ou mieux, des références :
int incluse (std::string & chaine1 ,std::string & chaine2){
  ...
}

Tu passes juste l'adresse en paramètre ce qui est donc plus rapide puisque tu ne recopies pas toute la chaîne. D'autre part ces chaînes ne varient pas à l'intérieur de la fonction est incluse. On peut donc garantir leur constance dans le prototype :
int incluse (const std::string & chaine1 ,const std::string & chaine2){
  ...
}

Ok maintenant il faut corriger deux trois choses, notamment la longueur d'une chaine s'obtient par :
std::size_t taille1=chaine1.size();

Un std::size_t est en réalité un unsigned int.

Pour extraire le morceau de chaîne qui nous intéresse on va utiliser la fonction substr :
https://en.cppreference.com/w/cpp/string/basic_string/substr
unsigned int incluse (
  const std::string & chaine1 ,
  const std::string & chaine2
){

  unsigned int compteur=0 , j=0;
  std::string sous_chaine2;

  for (unsigned int i=0 ; i<chaine2.size() ; i++) {
    //en fait on peut même s'arrêter à chaine2.size()-chaine1.size()
    sous_chaine2=chaine2.substr(i,chaine1.size());
    if(chaine1==sous_chaine2){
      //les std::string ont un opérateur == bien pratique ;)
      ++compteur;
    }
  }
  return compteur ;
}

Tu noteras que i est défini dans le for ce qui signifie que cette variable n'existera que tant qu'on sera dans le for. Outre le fait que tu gagnes en mémoire, ça évite certaines erreur. Je te conseille donc de fonctionner comme ça par la suite. Ceci dit ça ne marche qu'en C++.

Je n'ai pas compilé le code donc il comporte peut être quelques erreurs, mais je te laisse corriger, c'était juste pour te donner le début.

On peut encore améliorer le programme : la copie de sous_chaine2 fait perdre "beaucoup de temps", donc plutôt que d'utiliser substr, ont pourrait plutôt utiliser la méthode find() :
https://en.cppreference.com/w/cpp/string/basic_string/find
... et à chaque fois qu'une occurence de chaine1 est trouvée dans chaine2, déplacer le début de chaine2... Idéalement c'est comme ça qu'il faudrait faire. Je te laisse expérimenter tout ça ;)

Bonne chance
0
May
 
Est-ce qu'il y a une autre façon plus simple de le faire (avec les notions de base, car je n'ai pas encore tout vu ça en classe...) ?
Merci pour l'aide!
0
mamiemando Messages postés 33784 Date d'inscription   Statut Modérateur Dernière intervention   7 885
 
..........
0
May
 
umm... Je dois obligatoirement utiliser la fonction dont le prototype est int incluse (char chaine1[] , char chaine2[]); ... Vous l'avez changé pour int incluse (std::string chaine1 ,std::string chaine2){ ... Qu'est-ce que je peux faire maintenant? j Je suis tellement désespérée... =(
Mais merci pour l'aide quand même!
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
mamiemando Messages postés 33784 Date d'inscription   Statut Modérateur Dernière intervention   7 885
 
C'est pas grave ça :
int incluse (char _chaine1[] , char _chaine2[]){
  const std::string chaine1(_chaine1);
  const std::string chaine2(_chaine2);
  //et après tu manipules les strings
  //...
}


Mais bon on peut aussi utiliser strtok :
http://www.manpagez.com/missing.php
Ton séparateur est chaine1 et la recherche se fait dans chaine2.

Bonne chance
0
May
 
J'ai encore besoin d'aide...s'il vous plaît...
Je n'y arrive toujours pas...
0
mamiemando Messages postés 33784 Date d'inscription   Statut Modérateur Dernière intervention   7 885
 
Bon on va partir sur strtok().
http://www.manpagez.com/missing.php

- As-tu lu ce que ça faisait ?
- As-tu compris la notion de pointeur ?

Bonne chance
0
May
 
J'ai lu ce que strtok fait, mais je ne comprends pas trop. On n'a jamais vu les pointeurs, c'est ça le problème. Comme je te l'ai déjà dit, je suis vraiment débutante en C++. Il n'y a pas un autre moyen de réaliser la fonction sans cette notion?
Ma fonction de départ n'est vraiment pas bonne? On ne pourrait pas la corriger pour qu'elle fonctionne?
0
May
 
Je viens de refaire une autre fonction avec les notions que j'ai vues en classe. Elle a l'air correcte (pour moi), mais elle affaiche toujours « 0 » (zéro) pour le nombre d'occurences peu importe la séquence de caractères entrée au clavier :

#include <iostream>
#include <string>

using std::cout ;
using std::cin ;
using std::endl ;

int incluse (char chaine1[],char chaine2[]) {

int compteur = 0 ;
int i = 0 ;

while ( i < strlen(chaine2) ) {
if ( strncmp (chaine1 , chaine2 , strlen(chaine1)) == '0') {
compteur++ ;
i = i + strlen(chaine1) ;
}
else {
i = i + strlen(chaine1) ;
}}
return compteur ;
}



main () {

char chaine1[100] ;
char chaine2[100] ;
int compteur ;


cout << "Entrer une chaine de caracteres : \n" ;
cin >> chaine2 ;

cout << "Voici la chaine de caracteres que vous avez entre : \n" << chaine2 << endl ;

cout << "Entrer une sequence de caracteres a rechercher : \n" ;
cin >> chaine1 ;

compteur = incluse (chaine1 , chaine2) ;
cout << "Le nombre d'occurence de la 1re chaine dans la 2e est : \n" << compteur << endl ;

return 0 ;
}
0
mamiemando Messages postés 33784 Date d'inscription   Statut Modérateur Dernière intervention   7 885
 
strncmp (chaine1 , chaine2 , strlen(chaine1)) == 0
car '0' désigne le code ascii du caractère '0' qui ne vaut a priori pas 0.

Pour strtok, il suffit de suivre un exemple sur google :
https://codes-sources.commentcamarche.net/

L'idée c'est que ton pointeur va aller de séparateurs en séparateurs, en l'occurence la chaine que tu recherches. Il suffit donc ensuite de compter le nombre de séparateurs et c'est dans la poche.

Mais sinon ta solution avec strncmp marchera également c'est juste une manière différente de voir les choses.

Bonne chance
0
May
 
Bon...j'ai corrigé if ( strncmp (chaine1 , chaine2 , strlen(chaine1)) == '0') { pour if ( strncmp (chaine1 , chaine2 , strlen(chaine1)) == 0) { , mais mon programme m'affiche toujours un résultat bizarre.
Par exemple, si ma chaîne2 est : abcdeabcdghabcs
et que ma chaîne1 est : abc
le résultat sera : 5 (au lieu de 3)

et par exemple si ma chaine2 est : bonabcbonabcb
et ma chaine1 est : abc
le résultat sera : 0 (au lieu de 2)

Pourrais-tu me dire directement ce qui ne va pas dans ma fonction (la mienne), car j'ai besoin de remettre ce travail bientôt (dans un jour) et j'aimerais bien avoir la bonne réponse une fois pour toute...svp...
Merci
0
May
 
?????
0
May
 
Une dernière petite aide SVP !!!
0
May
 
Allo? Est-ce qu'il y a quelqu'un qui pourrait me répondre plzzzzzz!!!
0
mamiemando Messages postés 33784 Date d'inscription   Statut Modérateur Dernière intervention   7 885
 
Préliminaires

1) main retourne un int, donc c'est
:
int main(){
  ...
  return 0;
}

2) Soit tu fais un :
using namespace std;

soit tu écris std::cout, std::cin... Mais la tes using sont faux.

3) strlen retourne un unsigned int ce qui n'est pas le cas de i :
  unsigned int i = 0 ;
  while ( i < strlen(chaine2) ; i += strlen(chaine1) ) 

Note qu'en c++ tu peux déclarer i dans la boucle for ce qui permet de limiter son horizon, ce qui est bien d'un point de vue programmation. Ta boucle peut s'écrire :
for(unsigned int i=0;i<strlen(chaine2){
  if(strncmp (chaine1 , chaine2 , strlen(chaine1)) == 0) ++compteur
}

Pour éviter ce genre de problème compile par la suite avec les messages de warning. Par exemple sous linux :
g++ -W -Wall plop.cpp


4) Je ne comprends pas pourquoi i est incrément de strlen à chaque fois. Par exemple si tu cherches "abc" dans "kabckkk" tu vas louper le abc.

5) Enfin ton bug survient parce que tu ne fais jamais évoluer chaine1 et chaine2 donc à chaque passage de boucle ça refais la même chose. Au lieu d'incrémenter i il faut faire avancer le pointeur chaine2.

Corrigé

int incluse (char chaine1[],char chaine2[]) {

    int compteur = 0 ;
    unsigned int taille_chaine2 = strlen(chaine2);

    for(unsigned int i=0 ; i < taille_chaine2 ;++chaine2,++i ) {
        std::cout<<"compteur="<<compteur<<" i="<<i<<" chaine2="<<chaine2<<std::endl;
        if ( strncmp (chaine1 , chaine2 , strlen(chaine1)) == 0) {
            compteur++ ;
        }
    }
    return compteur ;
}

Tu noteras l'apparition taille_chaine2, très important puisque la taille de chaine2 va diminuer de 1 à chaque itération.

Exercices

Exercices :
1- résoudre le même problème avec strtok
2- résoudre le même problème avec les std::string

Le premier exo correspond à ce que tu devrais idéalement faire si tu programme en style C (c'est à dire ce que tu as choisi de faire).
Le second exo correspond à ce que tu devrais idéalement faire si tu programmais en style C++

Bonne chance
0
May
 
Merci beaucoup Mammiemando! Tu m'as beaucoup aidé!
=D
0