Lire un fichier, le chiffrer puis écrire dans un nouveau fichier en C++ [Résolu]

Signaler
-
 GrGrGr -
Bonjour,

Je souhaite, en codant une classe d'objet en c++, créer un programme capable de lire un fichier texte et de le chiffrer. Par chiffrement, j'entends un décalage de lettres: par exemple les 'a' en 'd', les 'b' en 'e', les 'c' en 'f', etc... Les lettres seront décalées suivant un décalage défini ("y" dans mon programme).
Le programme doit être capable d'écrire tout ce qu'il a chiffrer dans un nouveau fichier texte.

J'ai écrit un programme, mais il plante lorsque je le lance.

Voici mon code:
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

class Crypto{
private : static int x;
          static int y;
          static char c[40];
          static char d[400];
public : ofstream fichier_out(char c);
         ifstream fichier_in(char c);
         string fileName();
         Crypto();
         void Chiffrer(int y);
         void Dechiffrer(int y);
         void LireMessage();
};

string Crypto::fileName(){

    cout<<"quel est le nom de l'adresse ou se trouve le fichier ?"<<endl;
    fflush(stdin);
    cin.getline(c,40);
    return c;
}

ifstream Crypto:: fichier_in(char c){
    if(fichier_in(c)){
        x = 0;
      while(fichier_in(c).get(d[x])){
           fichier_in(c).get(d[x]);                                                                       //Acquisition du fichier origine dans d
            x++;
            }
          }
     else {
        cout<<"ERREUR : impossible d'ouvrir le fichier en lecture"<<endl;         //cas ou le fichier ne s'ouvre pas
    }

}

ofstream Crypto::fichier_out(char c){
    if(fichier_out(c)) {
    fichier_out(c) << d;                                    //ecriture du message codé
    }
    else {
        cout<<"ERREUR : impossible d'ouvrir le fichier pour ecriture"<<endl;      //cas ou le fichier ne s'ouvre pas
         }
}

void Crypto::Chiffrer(int y){
    cout<<"quel est le key number?"<<endl;
    fflush(stdin);
    cin>>y;
    for (x = 0; x < 400; x++){
         int spe[400];
         spe[x] = ((int) d[x])+y;        
         d[x] = spe[x];                                                  

     }
 }

void Crypto::Dechiffrer(int y){
    cout<<"quel est le key number?"<<endl;
    fflush(stdin);
    cin>>y;
    for (x = 0; x < 400; x++){
         int spe[400];
         spe[x] = ((int) d[x])-y;
         d[x] = spe[x];

  }
}

Crypto::Crypto(){
    fileName();
    fichier_in(c[40]);
    Chiffrer(y);
    fileName();
    fichier_out(c[40]);
}

int main(){
    Crypto a;
    getchar();
    return 0;
}


Je vous remercie.

(Edit admin: j'ai entièrement réécrit ton post, merci de bien respecter le format et d'écrire de manière plus rigoureuse la prochaine fois).

7 réponses

Messages postés
3838
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
19 février 2021
119
Bonjour !

Beaucoup de souci dans ce code !
Tout d'abord, on sent que tu n'as pas compris la notion de classe/objet. Tu utilises ton objet comme un programme, en le "tordant" pour tenter de faire quelque chose.
Ton objet devrait représenter une notion, pas ton programme. Ton "main" devrait manipuler ton objet qui ne devrait contenir que ton code de chiffrement.
Les interactions (std::cin et std::cout) sont à faire par le main. Ce n'est pas le rôle de ton objet.
Le constructeur de ta classe ne doit pas lancer ses propres méthodes et faire tout le travail ! Il ne sert qu'à initialiser. Tu lanceras les méthodes via ton "main".
Ta classe ne devrait avoir qu'un seul rôle: gérer le chiffrement d'une chaîne.

Ton code plante, car tu lui donne un caractère au lieu d'une chaîne de caractères, et que tu te mets à rappeler en boucle la même fonction, jusqu'à l'infinie (et la stack explose).
Ici:
ifstream Crypto:: fichier_in(char c){
    if(fichier_in(c)){

Tu appelles fichier_in, qui appelle fichier_in, qui appelle fichier_in, etc ...

Quelques remarques:

Voici une version "propre":
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <limits>

class Crypto
{
public:
    Crypto(const std::string& str, int key);
    void Chiffrer();
    void Dechiffrer();
    std::string String() const;

private:
    std::string _str;
    const int _key;
};

Crypto::Crypto(const std::string& str, int key)
    : _str(str), _key(key)
{
}

void Crypto::Chiffrer()
{
    for (unsigned int i = 0; i < _str.length(); ++i)
    {
        _str[i] += _key;
    }
}

void Crypto::Dechiffrer()
{
    for (unsigned int i = 0; i < _str.length(); ++i)
    {
        _str[i] -= _key;
    }
}

std::string Crypto::String() const
{
    return _str;
}

std::string fileToString(const std::ifstream& in)
{
    std::stringstream buffer;
    buffer << in.rdbuf();
    return buffer.str();
}

void flushCin()
{
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

int main()
{
    std::string filename;
    std::cout << "Quel est le nom de l'adresse ou se trouve le fichier ?" << std::endl;
    std::getline(std::cin, filename);

    std::ifstream file(filename);
    if (!file)
    {
        std::cerr << "Fichier non trouvé: " << filename << std::endl;
        return 1;
    }

    int key;
    std::cout << "Quel est le key number ?" << std::endl;
    std::cin >> key;

    Crypto crypto(fileToString(file), key);

    int action;
    std::cout << "1) Chiffrer\n"
                 "2) Dechiffrer\n"
              << std::endl;
    std::cin >> action;
    switch (action)
    {
    case 1:
        crypto.Chiffrer();
        break;
    case 2:
        crypto.Dechiffrer();
        break;
    default:
        std::cout << "Action inconnue, la chaine ne sera pas modifiee" << std::endl;
    }

    std::string outfile;
    std::cout << "Quel est le nom de l'adresse ou ecrire le fichier ?" << std::endl;
    flushCin();
    std::getline(std::cin, outfile);

    std::ofstream out(outfile);
    if (!out)
    {
        std::cerr << "Impssible d'ecrire dans: " << outfile << std::endl;
        return 1;
    }
    out << crypto.String();

    return 0;
}


Si tu as des questions, n'hésite pas !
Messages postés
15791
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
12 avril 2021
530
Bonjour

Deux conseils pour augmenter tes chances d’obtenir une réponse satisfaisante (en l’état, elles sont proches de 0)

Tout d’abord rendre ton code facile à lire en appliquant la procédure décrite là https://codes-sources.commentcamarche.net/faq/11288-les-balises-de-code

Ensuite
Mon programme plante lorsque je le lance...
ne sers pas à résoudre ton problème.
  • Y a t il un message d’erreur? Si oui le copier coller sera d’une grande aide?
  • la ligne concernée est elle identifiée?
  • le problème apparaît il à la compilation ou à l’exécution?
  • Si c’est à l’exécution, as tu exécuté en pas à pas, tout en espionnant le contenu des variables, pour observer ce qui se passe?
Merci beaucoup!
Votre réponse m'a beaucoup aidé!
Je ne comprends pas bien comment le constructeur "transmet" des paramètres vers _str avec std::string & str?

Aussi, j'ai repris votre code et j'essaye d'ajouter une fonction LireMessage mais lorsque je lance le programme, tout s'execute parfaitement sauf LereMessage : j'ai l'impression que dès que j'entre dans le switch le programme ferme.

Merci par avance pour votre aide précieuse.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <limits>

class Crypto
{
public:
    Crypto(const std::string& str, int key);
    void Chiffrer();
    void Dechiffrer();
    void LireMessage();
    std::string String() const;

private:
    std::string c;
    const int x;
};

Crypto::Crypto(const std::string& str, int key)
    : c(str), x(key)
{
}

void Crypto::Chiffrer()
{
    for (unsigned int i = 0; i < c.length(); i++)
    {
        c[i] += x;
    }
}

void Crypto::Dechiffrer()
{
    for (unsigned int i = 0; i < c.length(); i++)
    {
        c[i] -= x;
    }
}

std::string Crypto::String() const
{
    return c;
}

std::string fileToString(const std::ifstream& in)
{
    std::stringstream buffer;
    buffer << in.rdbuf();
    return buffer.str();
}

void flushCin()
{
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

void Crypto::LireMessage()
{
    for (unsigned i = 0; i < c.length(); i++)
    {
        std::cout << c[i];
    }
}

int main()
{
    std::string filename;
    std::cout << "Quel est le nom de l'adresse ou se trouve le fichier ?" << std::endl;
    std::getline(std::cin, filename);

    std::ifstream file(filename);
    if (!file)
    {
        std::cerr << "Fichier non trouvé: " << filename << std::endl;
        return 1;
    }

    int key;
    std::cout << "Quel est le key number ?" << std::endl;
    std::cin >> key;

    Crypto crypto(fileToString(file), key);

    int action;
    std::cout << "1) Chiffrer\n"
                 "2) Dechiffrer\n"
              << std::endl;
    std::cin >> action;

    int action2;
    std::cout << "Voudrez vous lire le fichier dans lequel le programme sera ecrit?\n"
                 "1) Oui\n"
                 "2) Non\n"
              << std::endl;
    std::cin >> action2;

    switch (action)
    {
    case 1:
        crypto.Chiffrer();
        break;
    case 2:
        crypto.Dechiffrer();
        break;
    default:
        std::cout << "Action inconnue, la chaine ne sera pas modifiee" << std::endl;
    }

    std::string outfile;
    std::cout << "Quel est le nom de l'adresse ou ecrire le fichier ?" << std::endl;
    flushCin();
    std::getline(std::cin, outfile);

    std::ofstream out(outfile);
    if (!out)
    {
        std::cerr << "Impssible d'ecrire dans: " << outfile << std::endl;
        return 1;
    }
    out << crypto.String();
    switch (action2)
    {
    case 1:
      crypto.LireMessage();
        break;
    case 2:
        std::cout << "Vous avez choisi de ne pas lire le fichier qui sera ecrit" << std::endl;
        break;
    default:
        std::cout << "Action inconnue, le fichier ne sera pas lu" << std::endl;
    }
    getchar();

    return 0;
}
Messages postés
3838
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
19 février 2021
119
Par convention, les attributs privés commencent par un "_". Par exemple "_str". Evite de nommer des attributs avec une seule lettre, c'est difficile à relire.

En gros, "const std::string&" veut dire: "std::string" sans le copier.

Un constructeur s'écrit soit (bof):
Crypto::Crypto(const std::string& str, int key)
{
   _str = str;
  _key = key; // si _key est "const", cette ligne ne fonctionnera pas
}


Soit (mieux):
Crypto::Crypto(const std::string& str, int key)
    : _str(str), _key(key) // fonctionne même si _key est const
{
}


Ton problème c'est que tu lis "action2", puis tu regardes "action", et après tu ne demandes pas "action2" et tu regardes "action2".
Tu devrais: Lire "action", regarder "action". Lire "action2", regarder "action2".

Quel est l'intérêt de LireMessage?
std::cout << crypto.String() << std::endl le ferait déjà.
Sauf que là, non seulement tu écris caractères par caractères dans la sortie standard (au lieu de pousser toute la chaîne d'un coup), mais en plus, tu "infectes" ta classe avec une notion de flux. Ta classe crypto n'a pas à savoir ce qu'est un flux (std::cout). Il manipule un std::string, le chiffre, déchiffre, et renvoie son état. Mais ce n'est pas son rôle d'écrire. C'est à celui qui manipule la classe Crypto d'afficher, d'écrire ou de faire une action avec le résultat de Crypto.
Attention, il faut bien comprendre que le but de l'objet, c'est de donner une responsabilité précise et bien définie à chacun. Si tout est mélangé et que tout le monde fait tout et a accès à tout, alors, le design sera très mauvais.
Prends bien l'habitude maintenant, de faire de l'objet correctement. Après, ça va être compliqué de perdre les mauvaises habitudes.
Bonjour,
Je vois que j'ai pris de mauvaises habitudes à essayer de mettre un maximum de fonctions dans un objet.
Je prends note de tous vos conseil merci.
Par contre je ne comprends toujours pas pourquoi le programme échoue lorsqu'il tente de tester action2?
Avec std::cin je fait l'acquisition du choix de l'utilisateur comme pour action 1 non?
J'ai encore du mal avec std::cin, avec le buffer (quand est ce qu'il faut le vider, comment le vider "proprement" etc...
Messages postés
3838
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
19 février 2021
119
Je viens de tester le programme. Chez moi il fonctionne bien. J'ai simplement mal regardé, et je n'avais pas vu la ligne "std::cin >> action". Oublie mon commentaire précédent. Qu'entends-tu par "ça ne fonctionne pas" ?

En revanche, j'aurais demandé s'il fallait lire le fichier, tout à la fin, juste avant de justement le lire. Ça serait plus logique et lisible.

Pour le flush, normalement, tu n'as pratiquement jamais besoin de le faire. Ici, on ne l'a pas fait, mais quand on code une routine ou on demande une entrée utilisateur on la sécurise en vérifiant si le type est le bon, s'assurant que l'entrée est valide, etc...

De plus un programme console s'utilise au final assez rarement comme cela. Très souvent, tu vas soit demander un fichier, soit prendre un flux d'entrée. Le but étant de chaîner les appels. Exemple: echo "mon text" | ./crypto.exe 5 > fichier_sortie.txt

Ici, il est clair que c'est un simple exercice pédagogique, donc tu n'as pas besoin de te prendre la tête là dessus. La seule chose à retenir, c'est que la gestion de l'entrée standard n'est pas la chose la plus importante.
Très bien, je vous remercie encore pour votre aide et pour vos conseils que je garde en tête pour la suite.
Bonne journée! :D