Indice pour réussir

Fermé
ik - Modifié le 25 août 2018 à 22:00
mamiemando
Messages postés
31324
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
19 août 2022
- 15 sept. 2018 à 18:13
Bonjour
voulant transformer ce code c en c++
un informaticien chevronné pourrait il le réécrire en mieux
svp
Cordialement

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <vector>
#include <map>
using namespace std;

// Foncteur qui effectue un chiffrement par decalage
class ChiffrementSubst{

public:

  // Constructeur prenant le nom fichier contenant les decalages a utiliser
  ChiffrementSubst(const string& nomFichier)
    :m_table()
  {
    ifstream fichier(nomFichier.c_str());

    for( int i(0); i<26; ++i)
      {
 char lettre, substitut;
 fichier >> lettre;
 fichier >> substitut;

 // On cree une nouvelle case dans la table
 m_table[lettre] = substitut;
      }
  }

  // L'operateur() qui effectue le chiffrement
  char operator()(char lettre)
  {
    // On transforme uniquement les caracteres majuscules
    if(isupper(lettre))
      {
 // On cherche la lettre dans la table
 return m_table[lettre];
      }
    else
      {
 return lettre;
      }
  }

private:

  map<char, char> m_table;  // La table contenant les caracteres cryptes

};


int main()
{

  // Le message a crypter
  string texte("BIENVENUE SUR LE MOOC C++ D'OCR !!");

  // Demande du decalage a l'utilisateur
  cout << "Quel fichier contenant la cle voulez-vous utiliser ? ";
  string nomFichier;
  cin >> nomFichier;

  // Creation du foncteur
  ChiffrementSubst foncteur(nomFichier);

  // Chaine de caracteres pour le message crypter
  string texte_crypte;

  // Un iterateur permettant l'insertion a la fin
  back_insert_iterator<string> it(texte_crypte);

  // On applique le foncteur sur le vector pour crypter le message en utilisant 'transform'
  transform(texte.begin(), texte.end(), it, foncteur);

  // Note, on aurait pu utiliser
  // texte_crypte.resize(texte.size());
  // transform(texte.begin(), texte.end(), texte_crypte.begin(), foncteur);
  // a la place du back_insert_iterator

  // On copie le message dans cout en utilisant l'algorithme 'copy'
  copy(texte_crypte.begin(), texte_crypte.end(), ostream_iterator<char>(cout, ", "));
  cout << endl;

  string pause;
  cin >> pause;


  return 0;
}

1 réponse

mamiemando
Messages postés
31324
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
19 août 2022
7 401
Modifié le 15 sept. 2018 à 18:20
Bonjour,

Ça me paraît bien (même si je n'ai pas pu tester le programme étant donné que je ne sais pas ce que dois contenir le fichier lu).

Voici ton code modifié (même si personnellement j'aurais choisi un autre format de fichier.)

cle.txt

AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYy


transform.cpp

#include <algorithm>    // std::transform
#include <cstdlib>      // EXIT_*
#include <iostream>     // std::cout, std::cerr
#include <fstream>      // std::ifstream
#include <ostream>      // std::ostream
#include <map>          // std::map
#include <string>       // std::string

#define DEFAULT_INPUT_TEXT "BIENVENUE SUR LE MOOC C++ D'OCR !!"

/**
 * @brief Traduit une std::map au format input_text.
 * @param os Le flux de sortie.
 * @param m La std::map à traduire.
 * @param os Le flux de sortie modifié.
 */
template <typename K, typename V>
std::ostream & operator << (std::ostream & os, std::map<K,V> m) {
    os << '{';
    for (auto p : m) {
        os << '\t' << p.first << " : " << p.second << std::endl;
    }
    os << '}';
    return os;
}

/**
 * @brief Lit un fichier et associe dans une std::map le 2*i ème caractère
 *   au 2*i+1 ème caractère, 0 <= i <= 26.
 * @brief Le chemin vers le fichier à lire.
 * @param La std::map à compléter.
 * @return true en cas de succès, false sinon.
 */
bool parse_key_file(const std::string & filename, std::map<char, char> & table) {
    bool ret = true;
    char lettre, substitut;
    std::ifstream fichier(filename);

    if (fichier) {
        for(int i = 0; i < 26; ++i) {
            fichier >> lettre >> substitut;
            table[lettre] = substitut;
        }
    } else {
        std::cerr << "Nom de fichier [" << filename << "] invalide" << std::endl;
        ret = false;
    }

    return ret;
}

/**
 * @brief Foncteur qui effectue un chiffrement par decalage
 */
class ChiffrementSubst{
    private:

        std::map<char, char> m_table;  /**< La table contenant les caracteres cryptes. */

    public:

        /**
         * @brief Constructeur.
         * @param Chemin absolu du fichier contenant les decalages à utiliser.
         */
        ChiffrementSubst(const std::map<char, char> & table):
            m_table(table)
        {}

        /**
         * @brief Traduit une lettre vers la lettre chiffrée.
         * @param lettre La lettre à chiffrer.
         * @return La lettre chiffrée.
         */
        inline char operator()(char lettre) const {
            std::map<char, char>::const_iterator fit(m_table.find(lettre));
            return fit != m_table.end() ? fit->second : '?';
        }
};

/**
 * @brief Programme principal.
 * @param argc Nombre d'arguments.
 * @param argv Arguments reçus en paramètres.
 */
int main(int argc, char ** argv) {
    if (argc >= 4) {
        std::cerr << "usage: " << argv[0] << " [filename [input_text]]" << std::endl;
        return EXIT_FAILURE;
    }

    // Demande du décalage à l'utilisateur
    std::string filename;
    if (argc >= 2) {
        filename = argv[1];
    } else {
        std::cout << "Quel fichier contenant la clé voulez-vous utiliser ? ";
        std::cin >> filename;
    }

    // Préparation du message à crypter
    std::string input_text = argc >= 3 ? argv[2] : DEFAULT_INPUT_TEXT;
    std::cout << "input_text = '" << input_text << "'" << std::endl;

    // Lecture du fichier d'entrée
    std::map<char, char> table;
    bool success = parse_key_file(filename, table);
    if (!success) {
        std::cerr << "Fichier d'entrée incorrect [" << filename << "]" << std::endl;
        return EXIT_FAILURE;
    }

    // DEBUG
    //std::cout << table << std::endl;

    // Création du foncteur
    ChiffrementSubst fonctor(table);

    // Chaine de caracteres pour le message chiffre
    std::string output_text;
    output_text.resize(input_text.size());

    // Un iterateur permettant l'insertion a la fin
    std::transform(input_text.begin(), input_text.end(), output_text.begin(), fonctor);
    std::cout << "output_text = '" << output_text << "'" << std::endl;
    return EXIT_SUCCESS;
}


Compilation

g++ -W -O2 transform.cpp -o transform


Exécution

./transform cle.txt BONJOUR


Résultat

input_text = 'BONJOUR'
output_text = 'bonjour'


Quelques conseils / remarques par rapport à ton code
  • Personnellement, je recommande au début de ne pas utiliser
    using namespace std;
    . Certes ça allège grandement l'écriture, mais il faut garder que ce genre d'instruction est à proscrire dans header (fichier .hpp). Certes ici on est dans un fichier source (.cpp) mais c'est pas mal au début de se graver dans l'esprit qui est dans quel namespace. En effet, ça prépare le terrain pour la suite quand on fait de la méta programmation (cf template) ou quand on utilise des objets provenant de différents namespace (e.g.
    std::
    ,
    boost::
    , etc.). Mais ce que tu as écrit est parfaitement correct et légitime.
  • Pas besoin de recopier la chaîne
    output_text
    pour l'afficher, tu peux l'utiliser directement. Ça économisera une recopie qui n'apporte rien dans ton cas. Pour
    std::ifstream ifs(filename.c_str())
    , pas besoin de convertir le chemin de
    std::string
    vers
    const char *
    .
  • Pour bénéficier des générateurs de documentations (e.g. doxygen) tu peux documenter tes fonctions/méthodes/classe comme indiqué ci-dessus.
  • En terme de design, la partie foncteur et lecture du fichier sont deux considérations indépendantes. En découplant les deux, comme je l'ai fait, tu te laisses possibilité d'obtenir
    table
    autrement (lecture sur
    std::cin
    , à partir d'un autre parser, par exemple un fichier json, etc...)
  • Il est préférable d'obtenir les paramètres du programme (dans ton cas, le chemin du fichier clé
    filename
    et le texte d'entrée
    input_text
    ) via les arguments du programme que via
    std::cin
    : cela permet de faciliter l'intégration de ton programme dans un autre programme (e.g. un script).
  • Attention à bien contrôler les cas d'erreur (chemin invalide, etc...) et à interrompre ton programme quand cela arrive. Une manière de faire est aussi de lever des exceptions (exemple :
    throw std::runtime_error("Erreur");
    )
  • Il vaut mieux prendre l'habitude de nommer ses variables en anglais plutôt qu'en français. Car dans la vraie vie, nos coéquipiers développeurs ne sont pas toujours français !
  • Si on pousse le truc à l'extrême (ce que je ne fais pas en temps normal), on peut définir les messages dans des
    #define
    , ce qui permet de les modifier à la compilation et d'envisager un programme en plusieurs langues, typiquement avec l'option -D si tu utilises
    g++
    !
  • On évite les "pause" comme tu as fait dans ton programme. Je sais que beaucoup de windowsien font ça pour pouvoir double cliquer sur leur programme et pour pouvoir lire le résultat, mais en vrai... ils devraient simplement ouvrir leur commandes ms-dos au bon endroit, puis y exécuter leur programme. C'est une considération "extérieure" au programme en tout cas.
  • Note que si tu travailles avec un IDE (e.g. code::blocks, devcpp, visual studio sous windows et anjuta ou [ https://doc.ubuntu-fr.org/kdevelop kdevelop] sous linux) ce genre de question ne se posera pas. C'est intéressant de travailler avec un IDE (coloration syntaxique, console et débogueur intégré, etc...). Après, un éditeur évolué (notepad++ sous windows,
    gedit
    ou
    kate
    sous linux) est souvent amplement suffisant pour débuter.


Bonne chance
1