Problème de conversion en string

Bonjour
Je bute sur un problème peut être bête :
J'ai une liste de prénoms définie comme cela :

char* noms[18];

qui va être remplie en utilisant un formulaire web (ça tournera sur un esp8266)

Je voudrais constituer un message du type :
"L'utilisateur TOTO a sonné"
et le publier via MQTT
J'ai donc le debut et la fin du message qui sont stockés dans un const char :

const char* MESSPARTS[]={"L'utilisateur ", "a sonné"};

J'ai essayé sans succès de former le message global, par exmple avec :

 String msgString(noms[0]);
 snprintf(msg, MSG_BUFFER_SIZE,"%s%s%s",MESSPARTS[0],msgString,MESSPARTS[1]);

Mais le prénom manque toujours au milieu du message.

Je précise qu'un Serial.print(noms[0]); affiche bien le premier prénom s'il est appelé dans le setup() mais pas dans le loop()

Pourriez vous éclairer ma lanterne svp ?
Merci

tu te pièges toi même en utilisant une String, dont tu n'as pas besoin.
snprintf ( msg, MSG_BUFFER_SIZE, "%s%s%s", MESSPARTS[0], noms[0], MESSPARTS[1]);
devrait faire l'affaire.

En écrivant le message initial, je le suis rendu compte que Serial.print(nom[0]); ne retourne rien dans la boucle principale, alors que ça retourne un prénom dans le setup()

Donc ce n'est même pas un problème de conversion finalement

Peut être une particularité liée aux pointeurs ?

Bonjour,

Il faudrait savoir comment nom[] est rempli. Car char* noms[18]; définit un tableau de pointeurs donc il faut que ces pointeurs pointent soit sur des chaines constantes soit sur des chaines allouées dynamiquement.

Pour ma pars, pour faire des messages d'erreur ou d'alerte, je fais plutôt ceci:

#define MSG_AVERTISSEMENT_1 "L'utilisateur %s a sonné!"
snprintf ( msg, MSG_BUFFER_SIZE, MSG_AVERTISSEMENT_1, noms[0]);

Sauf si les messages peuvent être une combinaison de plusieurs sous expression.

Sûrement pas. Mets ton prog en entier STP.

Je préfère ne pas mettre le programme en entier, car très très brouillon, mais voici comment est rempli le tableau noms[] :

for (uint8_t i = 2; i < cnt; i++) {
  
  
    char charBuf[50];
    conf.values[i].toCharArray(charBuf, 50) ; //à ce stade, charbuf est composé de "NOM/0605040301" 
 
    char *tampon2 = NULL;

    tampon2 = strtok(charBuf,"/");
    noms[i-2]=tampon2;

    Serial.print(noms[i-2]);
   
    tampon2 = strtok(NULL, "/"); 
    Serial.print(" -----  ");
    
    numeros[i-2]=atoi(tampon2);
    Serial.println(numeros[i-2]);
  }

J'ai l'impression qu'il me faut utiliser autre chose qu'un tableau de pointeurs pour noms[]

tampon2 est une variable locale qui sera détruite lorsque tu sortiras de la boucle for

ne copie pas le contenu. Cette affectation ne fait que placer un pointeur dans le tableau noms. Pointeur qui ne pointera plus sur rien à la fin de la boucle for

Ok, et donc il faut que je déclare tampon2 au niveau global ?
Est-ce suffisant ?

Non, puisque tu fais une boucle et qu'à chaque itération de la boucle tu vas changer le contenu de la variable.
Il faut avoir autant de variables distinctes que de places dans ton tableau de nom.
Généralement, pour faire ce genre de chose on fait de l'allocation dynamique. Lorsqu'on ajoute une valeur dans le tableau, on alloue avec malloc une zone mémoire pour stocker le contenu et c'est le pointeur retourné par malloc que l'on place dans le tableau. Sur des CPU avec peu de mémoire comme les cartes Arduino style UNO c'est risqué car on peut vite utiliser toute la mémoire et il n'en reste plus pour faire tourner le programme.
Sinon tu as la possibilité de déclarer un tableau à 2 dimensions pour nom. Par exemple noms[18][12] ce qui permettra de créer 18 noms de 11 caractères. Cela à 2 points faibles:

  • la taille des noms est donc limitée par la taille déclarée à la création du tableau, dans l'exemple ne noms ne pourront pas faire plus de 11 caractères sinon on va écraser des informations.
  • on peut gâcher de la mémoire si on a beaucoup de noms courts et peu de noms longs puisqu'on déclare une longueur commune à tous.

Dans le premier cas la mémoire est mieux utilisé mais on risque de consommer toute la mémoire sans s'en apercevoir. Sur des petits CPU cela fonctionne si le nombre de valeurs à créer est réduit et si on ne modifie pas leur contenu dans le programme.
Dans le second cas la mémoire n'est pas utilisée au mieux et on a certaines contraintes mais on sait dès le début (à la compilation) si on va tenir en mémoire.

Ok, je préfère la solution 1
J'ai donc défini mon tableau de noms comme ceci :

char noms[18][11];

et le remplis comme cela :

for (uint8_t i = 2; i < cnt; i++) {
  
   
    char charBuf[50];
    conf.values[i].toCharArray(charBuf, 50) ;
   
    char* tampon2 = NULL;
    tampon2 = strtok(charBuf,"/");
    strcpy(noms[i-2],tampon2);
   
    Serial.print(noms[i-2]);
   
    tampon2 = strtok(NULL, "/"); 
    Serial.print(" -----  ");
    
    numeros[i-2]=atoi(tampon2);
    Serial.println(numeros[i-2]);
  }

Et là ça marche !
Merci

Par sécurité, il vaut mieux utiliser strncpy(char *dest, const char *src, size_t n) qui évite les débordements si la chaîne à copier est trop longue.
strncpy(noms[i-2],tampon2, 10);

Si tu manipules toujours nom et numéro ensemble, tu pourrais créer une structure.

struct fiche{
    char nom[11];
    int numero;
}; 

et ensuite tu déclares

fiche fichier[18];

Et tu peux l'utiliser comme ça

struct fiche{
    char nom[11];
    int numero;
}; 

fiche fichier[18];

void setup() {
  Serial.begin(115200);
   // remplissage du fichier
  strncpy(fichier[0].nom, "premier nom", 10);
  fichier[0].nom[10]=0; // au cas ou la chaine serait trop longue
  fichier[0].numero = 12345;
  strncpy(fichier[1].nom, "second nom", 10);
  fichier[1].nom[10]=0;
  fichier[1].numero = 23456;

  //  lecture du fichier
  for(int i = 0; i<2; i++){
    Serial.print(fichier[i].nom);
    Serial.print(" : ");
    Serial.println(fichier[i].numero);
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

Ok, merci, mais tu as fait une erreur, c'est strncpy et non strcpy :smiley:
Pour la structure, je vais m'y pencher, c'est sur que ce serait plus propre

strtok fait une copie de la chaine de travaille dans la pile, du coup l'espace pointé sera libéré lorsque tu passe à une autre chaine ou à la sortie de la fonction, si tu en utilise une.
Peut être que ton problème venais de là.

Il est possible de se passer de la copie des chaines en utilisant le pointeur sur la chaine conf.values[i] et en remplaçant le caractère '/' par la valeur 0.
Mais cela vaut il vraiment le coup de se prendre la tête si ça marche très bien comme ça :slight_smile:

C'est corrigé

Merci encore à tous
Et je suis d'avis de ne pas me prendre la tête :slight_smile: