Problème chiffrement en Morse [Résolu]

Signaler
Messages postés
9
Date d'inscription
dimanche 15 décembre 2013
Statut
Membre
Dernière intervention
10 décembre 2016
-
Messages postés
3840
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
11 juin 2021
-
Bonjour,
Je souhaitais créer un programme permettant de transformer une chaine de caractères (un mot) entrée au clavier par l'utilisateur et afficher ensuite à l'écran son équivalent morse.
Je pense que mon code n'est surement pas le plus simple ou le plus efficace mais malgré cela je ne vois pas pourquoi celui-ci ne fonctionne pas !
La compilation se passe bien, toutes les étapes donnent l'impression de se dérouler normalement mais l'équivalent morse affiché à l'écran n'est pas du tout bon.
J'ai utilisé un fichier main.h contenant la définition d'un type tableau de codage reliant chaque caractère à son équivalent morse :
typedef struct codage codage ;
    struct codage {
        char c ;
        char ch[TAILLECODAGE] ;
    };
typedef codage tabcodage[TAILLEALPHABET] ;



Je l'ai inclus à mon fichier main.c suivant :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAILLECODAGE 5 //Taille maximale de l'équivalent morse d'une lettre
#define TAILLEALPHABET 26 
#define TAILLEMOTMAX 30 
#include "main.h" // Fichier contenant le type tableau codage 

void precalculeLgCodage (tabcodage monCode, int taillesCodes[TAILLEALPHABET]) 
{
    int i ;
    for (i=0; i<TAILLEALPHABET;i++)
        taillesCodes[i] = strlen(monCode[i].ch);
} // Remplit le tableau taillesCodes contenant la taille de l'équivalent morse de chaque lettre

int MorseEncodingLength(char *mot, int taillesCodes[TAILLEALPHABET])
{
    int i,res ;
    i = 0 ;
    res = 0 ;
    while (mot[i]!='\0')
    {
        res = res + taillesCodes[*(mot+i)-'a']+1;//pour l'espace séparateur
        i++;
    }
    return (res) ;
} // Retourne la taille de la chaine morse équivalente à la chaine mot 

char* MorseEnCode(char *mot,tabcodage monCode, int taillesCodes[TAILLEALPHABET])
{
    char* motMorse ;
    int i ;
    int lgMotMorse, lgMot ;
    lgMot = strlen(mot) ;
    lgMotMorse = MorseEncodingLength(mot, taillesCodes) ;
    motMorse = malloc(lgMotMorse*sizeof(char)) ;
    if (motMorse == NULL)
        return NULL ;
    strcpy(motMorse,monCode[mot[0]].ch);
    for (i = 1; i<lgMot; i++);
    {
        strcat(motMorse,monCode[mot[i]].ch) ;
        if (i!= lgMot-1)
            strcat (motMorse, " ");
    }
    return motMorse ;
} //  Retourne la chaine morse équivalente à la chaine mot

int main()
{
    int taillesCodes[TAILLEALPHABET],encore;
    char mot[50] ;
    char *motMorse ;
    // Initialisation du tableau d'équivalence
    tabcodage morse = {
    {'a',".-"},
    {'b',"-..."},
    {'c',"-.-."},
    {'d',"-.."},
    {'e',"."},
    {'f',"..-."},
    {'g',"--."},
    {'h',"...."},
    {'i',".."},
    {'j',".---"},
    {'k',"-.-"},
    {'l',".-.."},
    {'m',"--"},
    {'n',"-."},
    {'o',"---"},
    {'p',".--."},
    {'q',"--.-"},
    {'r',".-."},
    {'s',"..."},
    {'t',"-"},
    {'u',"..-"},
    {'v',"...-"},
    {'w',".--"},
    {'x',"-..-"},
    {'y',"-.--"},
    {'z',"--.."}}; 

    precalculeLgCodage(morse,taillesCodes);
    encore = 1 ; // Permet de ne sortir du programme que si l'utilisateur tape "."
    do
    {
        printf("Tapez une phrase : ");
        scanf("%s", mot) ;
        if((mot[0]!='.')||(mot[1]!='\0'))
        {
            motMorse = MorseEnCode(mot,morse,taillesCodes);
            printf("Codage Morse : %s\n",motMorse);
        }
        else
        {
            encore = 0 ;
            printf("Au revoir !\n");
        }
    }
    while (encore);
    return 0;
}


D'après les différents tests que j'ai effectué le problème viendrait surement de codage en morse et plus précisément de la concaténation mais j'avoue ne pas trouver le problème malgré plusieurs heures à batailler dessus.
En tout cas je vous remercie vraiment si vous trouvez le courage de vous intéresser à mon code et de chercher à résoudre mon problème.
Je me tiens à votre disposition si vous souhaitez des informations sur mon programme.

9 réponses

Messages postés
3840
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
11 juin 2021
121
Bonjour.

Je regarde en détails, et te fais une réponse propre dans quelques minutes.
A première vu, ceci me "choque":

for (i = 1; i<lgMot; i++);

Le ; n'est surement pas là exprès, et c'est un souci :)
Messages postés
9
Date d'inscription
dimanche 15 décembre 2013
Statut
Membre
Dernière intervention
10 décembre 2016

Merci de t'intéresser à mon problème !!
En effet, c'est bien une erreur ! Mais je ne crois pas que ça fonctionne malgré tout !

J'attends ta réponse avec impatience. :)
Messages postés
3840
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
11 juin 2021
121
J'ai trouvé le souci :).

Y a une portion de code ou tu utilises ton tableau comme s'il était un tableau associatif, ce qui n'est pas le cas.
Ex:
monCode[0] => ".-"
mot[0] => 'a'
Donc monCode[mot[0]] => monCode['a'] => monCode[97] => n'importe quoi.
Il faut donc faire: monCode[mot[0] - 'a'] => monCode['a' - 'a'] => monCode[0] => ".-"

  strcpy(motMorse,monCode[mot[0] - 'a'].ch);
  for (i = 1; i < lgMot; ++i)
  {
    strcat(motMorse,monCode[mot[i] - 'a'].ch) ;


Pour le code, il est perfectible. J'y reviendrais plus tard, mais il y a deux-trois trucs améliorables.
Messages postés
9
Date d'inscription
dimanche 15 décembre 2013
Statut
Membre
Dernière intervention
10 décembre 2016

Ah oui en effet, j'ai écrit mon programme comme si le code ASCII de 'a' correspondait à la première case de mon tableau mais c'est assez stupide.

Merci beaucoup en tout cas, il fonctionne du coup !

Tu aurais pensé à quoi en termes d'améliorations possibles ? Ça me permettrait de travailler un petit peu plus !

Après pour réaliser mon programme je me suis servi d'un exercice avec des fonctions données à réaliser. En terme d'amélioration l'exercice proposait notamment la possibilité d'utiliser "un tableau dynamique de pointeurs sur caractères qui contiendra, dans chaque case, l'adresse du codage devant remplacer le caractère correspondant dans le mot à encoder" pour la fonction MorseEnCode. Mais j'ai préféré garder cette étape pour la fin vu que je ne suis pas encore très à l'aise avec l'allocation dynamique du fait que je débute.

En tout cas merci beaucoup de ton aide :)
Messages postés
3840
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
11 juin 2021
121
Autant séparé ton code en fichier est une bonne idée, autant la séparation doit être fait sans "ordre". J'entends par là que tu as des defines qui affecte le fichier inclus. Ce qui n'est pas très judicieux. Les defines devrait plutôt être dans le "main.h".
Pense à bien initialiser tes variables, ça peut te sembler inutile, mais c'est une sécurité/bonne pratique.
Attention les "//" ne sont pas de commentaires de code valides en C ansi. Elles le deviennent en C99, mais dans ce cas, tu as aussi accès aux déclarations de variables où tu veux (comme en C++). J'ai supposé que tu faisais du C89 ansi. Si tu fais du C99, alors tu n'as pas besoin de mettre toutes variables en "haut", tu peux les mettres où tu veux. Autre fait notable, en C ansi, tu n'es pas obligé de mettre tes variables en haut d'une fonction, mais au début d'un scope, quelqu'y soit (en d'autre terme, au début d'un bloc d'accolades, ce qui inclus les fonctions).

Comme ton tableau de morse ne changera jamais, on peut en faire une constante globale (une variable globale c'est moche, mais une constante globale n'est pas un problème, c'est différent).
Pas besoin de calculer les tailles. On peut les déduire grace au '\0' de fin de chaînes.
Au lieu de précalculer les tailles de chaques caractères, on peut considérer que tous les caractères prennent le maximum de taille (5). Ensuite, réduit la taille du tableau final pour qu'elle soit ajustée (via realloc).
Je transforme une majuscule en minuscule via "| 32", qui est la manière la plus rapide de passer de majuscule à minuscule (et ne fais rien si c'est déjà en minuscule).
J'ignore aussi les caractères non alphabétiques (je check que pos soit une valeur acceptable dans "morse". Comme le tableau est constant, on peut utiliser sizeof pour en déduire sa taille, sans faire de boucle, juste une division).
Je libère la mémoire via "free", ce que tu semblais avoir oublié dans ton code :p.
Je limite le nombre de caractère lisible via scanf, avec le format "%50s" au lieu de "%s", ça évite un débordement mémoire, si quelqu'un tape plus de 50 caractères.

Voici mon code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TAILLECODAGE 5

typedef struct
{
  char c;
  char ch[TAILLECODAGE];
} Codage;

static const Codage morse[] =
  {
    {'a',".-"},
    {'b',"-..."},
    {'c',"-.-."},
    {'d',"-.."},
    {'e',"."},
    {'f',"..-."},
    {'g',"--."},
    {'h',"...."},
    {'i',".."},
    {'j',".---"},
    {'k',"-.-"},
    {'l',".-.."},
    {'m',"--"},
    {'n',"-."},
    {'o',"---"},
    {'p',".--."},
    {'q',"--.-"},
    {'r',".-."},
    {'s',"..."},
    {'t',"-"},
    {'u',"..-"},
    {'v',"...-"},
    {'w',".--"},
    {'x',"-..-"},
    {'y',"-.--"},
    {'z',"--.."}
  };

char* morseEnCode(char* mot)
{
  char* motMorse = NULL;
  char* res = NULL;
  int i = 0;
  int lgMot = 0;

  lgMot = strlen(mot);
  motMorse = malloc(lgMot * TAILLECODAGE * sizeof (char));
  if (motMorse == NULL)
    return NULL;

  for (i = 0; i < lgMot; ++i)
  {
    const unsigned int pos = (mot[i] | 32) - 'a';
    if (pos < (sizeof (morse) / sizeof (*morse)))
    {
      strcat(motMorse, morse[pos].ch);
      if (i < lgMot - 1)
        strcat(motMorse, " ");
    }
  }

  res = realloc(motMorse, strlen(motMorse));
  if (res == NULL)
  {
    free(motMorse);
    return motMorse;
  }

  return res;
}

int main(void)
{
  int encore = 1;
  char mot[50] = {0};

  do
  {
    printf("Tapez une phrase : ");
    scanf("%50s", mot) ;
    if (mot[0] != '.' || mot[1] != '\0')
    {
      char* motMorse = NULL;
      motMorse = morseEnCode(mot);
      printf("Codage Morse : %s\n", motMorse);
      free(motMorse);
    }
    else
    {
      encore = 0 ;
      printf("Au revoir !\n");
    }
  }
  while (encore);

  return 0;
}


N'hésites pas si tu as des questions.
Messages postés
9
Date d'inscription
dimanche 15 décembre 2013
Statut
Membre
Dernière intervention
10 décembre 2016

Tout d'abord merci beaucoup pour ta réponse complète, détaillé, rapide, bref parfaite ! :)
Oui c'est vrai que j'aurais du mettre mes defines directement dans ma bibliothèque ! Je ne sais pas si pour un projet d'une taille aussi petite mon fichier .h est utile de toute façon.

En ce qui concerne l'initialisation des variables, il est vrai que j'ai tendance à ne pas le faire quand je sais que de toute façon elles seront initialisées dans ma fonction. Qu'est-ce que cela apporte comme sécurité supplémentaire ?
J'ai aussi tendance à toujours les déclarer en haut des fonctions (simple habitude), je ne savais même pas que je pouvais le faire au milieu en haut des blocs.

Pour le type tableau de codage, à quoi sert le static ? Si je me souviens bien, le static sert à conserver les valeurs modifiés entre les différents appels, mais alors vu que notre tableau morse est une constant, le mot clé static sert-il vraiment à quelque chose ?

Le calcul des tailles était une des fonctions proposées mais j'avoue que ta méthode est bien plus simple ;)
Par contre comment fonctionne le | de |32 ? 32 c'est le décalage de code ASCII entre 'A' et 'a' si je me trompe pas, le | effectue ce décalage dans le cas des majuscules ?

J'ai bien aimé ta manière de vérifier l'appartenance de chaque lettre au tableau de correspondance ! Mais honnêtement j'y aurais jamais pensé :p
Du coup je me demandais si cela fonctionnait ? :
if (pos<(26-'a'))

J'ai rajouté un cas pour remplacer strcat par strcpy parce que sinon j'obtenais un caractère étrange au début de chaque codage morse en lançant le programme.

Pour finir, pour le free, en effet je l'ai totalement oublié, c'est pas bien !! :p

En tout cas encore merci pour tout, le temps que tu as consacré à mon programme et tout, ça m'a bien aidé et puis j'aurais appris pas mal de choses.
Messages postés
3840
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
11 juin 2021
121
En ce qui concerne l'initialisation des variables, il est vrai que j'ai tendance à ne pas le faire quand je sais que de toute façon elles seront initialisées dans ma fonction. Qu'est-ce que cela apporte comme sécurité supplémentaire ?
Ca te protège de toi même :). On se dit souvent: "Non, mais je sais ce que je fais, pas besoin". Or un bug, est par définition une erreur involontaire, voir une petite étourderie. Quand tu oublies d'initialiser un pointeur par exemple, la valeur est alors aléatoire (en fait pas tout à fait, ça vaut la valeur du résidu mémoire où tu pointes).
Si tu utilises un pointeur intialisé à 0 (ou NULL c'est pareil), le programme plante immédiatement, et à l'endroit de l'utilisation. Si tu ne l'initialises pas, ça peut planter (ou pas du tout), ça peut planter par "à coup" ou en changeant de machine. Ca peut aussi et surtout planter bien plus loin dans une partie de code qui n'a rien à voir.
Pire encore, tu peux pointer tout à fait par hasard sur une zone valide, que tu vas écraser. La résolution du bug va alors être particulièrement épineuse.
Initialiser ses variables est une barrière de protection contre une erreur humaine, tout simplement. Je t'invite à en abuser !

Pour le type tableau de codage, à quoi sert le static ?
En C et en C++, les mots clés ont souvent plusieurs significations. "static" en dehors d'une fonction signifie: "J'assure que ce morceau de code ne sera pas visible en dehors du fichier où il est utilisé". Si je devais faire les choses proprement, je changerais d'ailleurs le code posté pour mettre un "static" devant toutes les fonctions (sauf le main). C'est une habitude que j'ai perdu en C++ (le mécanisme est différent).
C'est encore une barrière de sécurité supplémentaire. Le langage C est un des plus permissif qui soit, il est important d'en placer un maximum.

Par contre comment fonctionne le | de |32 ? 32 c'est le décalage de code ASCII entre 'A' et 'a' si je me trompe pas, le | effectue ce décalage dans le cas des majuscules ?
C'est une astuce un peu compliqué, qui fait appel au calcul binaire. Je vais essayer de t'expliquer, mais c'est peut être un peu en avance sur ton apprentissage.

Le | est un OU binaire.
Le & est un ET binaire.
Le ^ est OU EXCLUSIF binaire.
Le ~ est une INVERSION binaire.
Le ! est une NEGATION binaire.
Le << est un DECALAGE GAUCHE binaire.
Le >> est un DECALAGE DROITE binaire.

On ne va s'intéresser qu'au OU binaire (le |).
Soit la table du OU:

1 0
--------
1 | 1 1
0 | 1 0

Soit 'a' qui est égale à 97 et 'A' qui est égale à 65.
En binaire, ça donne ceci:

97 => 0110 0001
65 => 0100 0001
32 => 0010 0000

Si je fais 'a' | 32:

97 => 0110 0001
OR
32 => 0010 0000
-------------------
0110 0001 => 97
Donc: 'a' | 32 => 'a'

Si je fais 'A' | 32:

65 => 0100 0001
OR
32 => 0010 0000
-------------------
0110 0001 => 97
Donc: 'A' | 32 => 'a'

Du coup je me demandais si cela fonctionnait ? :
if (pos<(26-'a'))
Vu que 'a' vaut 97, tu auras une valeur négative (-71). Comme pos est non signé, tu passe à (2^32 - 71), donc pos a de grande chance d'être inférieur à cette immense valeur.
Messages postés
9
Date d'inscription
dimanche 15 décembre 2013
Statut
Membre
Dernière intervention
10 décembre 2016

Merci beaucoup pour tes précisions et désolé de pas avoir répondu plus tôt !

C'est vrai que je ne fais surement pas assez attention à bien sécuriser mon code, j'ai tendance à essayer d'aller droit au but et une fois que j'ai quelque chose qui fonctionne, je suis content :p

J'ai très bien compris pour le OU binaire, ton explication était très claire ! Merci beaucoup ! Je pense que cette astuce me servira plus d'une fois ! Si je me trompe pas ça fonctionne parce que dans les majuscules la case 32 vaut toujours 0 ! Est-ce que tu aurais un équivalent pour l'autre sens (transformer les min en maj et garder les maj ?

En effet ma tentative pour pos n'est pas très valable ! :)

Encore merci pour tout !
Messages postés
3840
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
11 juin 2021
121
Si je me trompe pas ça fonctionne parce que dans les majuscules la case 32 vaut toujours 0 !
Ce n'est pas la case 32, mais la case 5 en partant de la droite (si 0 est la première case). 32 s'écrit d'ailleurs aussi (1 << 5).


Est-ce que tu aurais un équivalent pour l'autre sens (transformer les min en maj et garder les maj ?

Pour faire le contraire, c'est plus compliqué, raison pour laquelle on préfère mettre en minuscule.

Prenons cette fois le ET:

97 => 0110 0001
ET
32 => 0010 0000
-------------------
0010 0000 => 32

Aie, comme tu peux le voir, ce n'est pas simplement appliquer un ET.
Là ou c'est un peu compliqué, c'est que le calcul théorique que je t'ai montré est faux en programmation.
En effet, je ne te montre pas les 0 inutiles, mais si je devais afficher en binaire sur entier signé de 32 bits, alors je devrais écrire ceci:

97 => 0000 0000 0000 0000 0000 0000 0110 0001
ET
32 => 0000 0000 0000 0000 0000 0000 0010 0000
-------------------
0000 0000 0000 0000 0000 0000 0010 0000 => 32
Donc, si tu avais 2145 (Juste un 1 devant le premier 1 binaire de 97), alors 2145 & 32 te donnera 32 aussi !
En effet, le & 32 te permet uniquement de savoir si le bit 5 est "allumé" ou non. C'est utile pour savoir si un nombre est pair, par exemple. Puisque si le bit 0 est allumé c'est un nombre pair, sinon impair. Ex:
0 => 000
1 => 001
2 => 010
3 => 011
4 => 100
Donc if (a & 1 == 0) printf("a est pair\n");
On utilise aussi beaucoup cette technique pour faire des flags d'option (notion un peu avancé, sur le même principe à coup de & et de |). Mais je ne vais pas trop déborder :).


Donc pour choisir de "n'éteindre" que le bit 5, il faut tout inverser (négation ~) et faire un ET.
Soit:

97 => 0000 0000 0000 0000 0000 0000 0110 0001
ET
~32=> 1111 1111 1111 1111 1111 1111 1101 1111
-------------------
0000 0000 0000 0000 0000 0000 0100 0001 => 65

Donc le contraire de 'a' | 32 est 'a' & ~32.