[ESP32) connaitre la longueur d'une chaine avec deux pointeurs

Bonjour,
J'aimerais connaitre la longueur d'une chaine avec deux pointeurs,
sans utiliser strlen, string etc.

si j'ai une chaine

int len = 0;
char chaine[] = "maison\r\n";
char *p = chaine;
char *q = nullptr;

q = strchr((const char *)p, '\n');
if (q  != NULL)
    len =  strchr((const char *)p, '\n') -p;

Est ce que
len me retoune 6 sans le '\r' ou est ce que

len me retourne 7 avec le '\r' inclut ?

Merci de votre aide.

J'aurais pu simplifier comme cela:

len = (q = strchr((const char *)p, '\n')) ? q -p : 0;

strlen() compte le nombre de caractères jusqu'au caractère null (que vous ne voyez pas) qui marque la fin de la c-string. Ici "maison\r\n" c'est 8 caractères mais le tableau chaîne en comporte 9, celui en plus c'est justement ce marqueur de fin de chaîne.

la soustraction de 2 pointeurs est OK donc si q est un pointeur sur caractère qui pointe quelque part dans la chaine, q - chaine c'est la distance entre les deux (donc + 1 pour le compte de caractères)

un exemple


le code

char chaine[] = "maison\r\n";

void trouve(char c) {
  char * q = strchr(chaine, c);
  if (q != nullptr) Serial.println(q - chaine + 1);
  else Serial.println("pas trouvé");
}

void setup() {
  Serial.begin(115200);
  Serial.print(F("Longueur de chaine = "));
  Serial.println(strlen(chaine));
  // ---
  trouve('m');   // 1er caractère
  trouve('n');   // 6ème caractère
  trouve('\n');  // 8ème caractère
  trouve('x');   // inexistant
}

void loop() {}

merci pour l'information,
Pour la demonstation j'ai mis '\r\n' mais dans mon cas présent ma chaine ne contient que des '\n' et pas de 0 final, c'est une chaine qui contient un fichier chargé dans un buffer en psram.
je lit le fichier caractère par caractères que je met dans le buffer, je n'ai pas ajouté exprès le 0 à la fin.

mais ma question était de savoir si le résultat de la longueur prenait en compte le '\n' ou pas ?

si vous n'avez pas de nul final vous ne pouvez pas utiliser strchr() car il ne sait pas où s'arrêter en mémoire...
faut coder cela à la main

si vous avez des caractères UTF8 (éàçè§ôö..) il faut peut-être aussi en tenir compte car ils peuvent être codés sur plusieurs octets, tout dépend exactement de votre objectif

c'est un caractère comme un autre donc il est compté avec strlen() mais pour utiliser strlen() il faut avoir une c-string, donc le nul terminal

strlen() compte le nombre d'octets depuis le pointeur que vous donnez jusqu'à rencontrer un caractère nul.

comme c'est un programme console je n'ai pas de caractère utf8, il y a un caractere '\n' pour chaque fin de ligne, mais en effet pour la dernière ligne je règlerai cela, là je suppose que la chaine est au milieu .
C'est juste pour savoir si q - p me retourne 6 ou 7 (je n'utilise pas strlen)

Si p et q sont des pointeurs sur un caractère?

S'ils pointent sur le même caractère, leur distance est 0 donc la soustraction est bien 0
si vous considérez que les caractères entre les 2 pointeurs (y compris) sont à prendre et que q est après p en mémoire alors q-p+1 c'est le nombre de caractères

éventuellement : Introduction à la mémoire et aux pointeurs sur Arduino

PS/ je le redis si pas de caractère nul, n'utilisez aucune fonction du genre strlen, strcpy, strchr, ... elles sont faites pour les c-strings

ah ok
donc q-p+1 c'est la longueur de la chaine y compris le '\n' ?
et q-p c'est sans le '\n' avec le strchr.

euh ben ça dépend où pointent p et q

oui mais le buffer est un fichier:

maison\ncadeaux\nbateau\nbijoux\n...

, sauf pour le dernier car.
le strchr fonctionne sur le pointeur du debut de chaine pour passer a la prochaine ligne.

p pointe au debut de la chaine
et q c'est le car. '\n' trouvé avec strchr, meme si la chaine n'a pas de 0 final.

S'il est en mémoire, c'est un buffer...

vous prenez un risque d'appeler strchr() si vous n'avez pas mis de nul à la fin car la fonction va potentiellement aller lire de la mémoire qui ne lui appartient pas ➜ en C++ c'est undefined behavior.

mettez le nul à la fin et ne vous ennuyez pas... ce sera super simple pour compter ou extraire chacun des mots

oui je suis d'accord avec vous , j'ai eu des problèmes de dépassement, je vais ajouter le 0 final
par contre pour le strrchr, donc l'inverse de strchr, arrivé au premier mot de la chaine je vais avoir un problème, car il n'y a pas de '\n' au début du fichier, à moins que j'en ajoute un

je ne sais pas ce que vous voulez faire

par exemple pour compter les mots (avec ou sans \n à la fin)

char fichier1[] = "";
char fichier2[] = "maison";
char fichier3[] = "maison\ncadeaux";
char fichier4[] = "maison\ncadeaux\nbateau\nbijoux\n";

size_t compteMots(const char * s) {
  size_t n = *s == '\0' ? 0 : 1;
  while (*s)
    if ((*s++ == '\n') && (*s != '\0')) n++;
  return n;
}

void setup() {
  Serial.begin(115200);
  Serial.print(compteMots(fichier1));  Serial.println(" mots");
  Serial.print(compteMots(fichier2));  Serial.println(" mots");
  Serial.print(compteMots(fichier3));  Serial.println(" mots");
  Serial.print(compteMots(fichier4));  Serial.println(" mots");
}

void loop() {}

ça vous dira

0 mots
1 mots
2 mots
4 mots

à tester dans le simulateur

oui je n'ai pas expliquer car je m'était concentrer sur ma question mais en fait:
j'ai fait un éditeur hexa mode console (escape ansi) avec gestion des touches Up, Dn, PgUp, PgDn qui fonctionne très bien et maintenant je voulais faire un éditeur de texte mais vraiment minimal, juste l'afficher sur 22 ligne et déplacer le texte avec les touches flèche bas, haut, page up, page down.

tout cela avec des pointeurs, d'ou le strchr pour récupérer la prochaine ligne, qui fonctionne dans ce sens mais l'inverse (strrchr) je n'ai pas réussià le faire fonctionner, soit j'ai des caractère bizarre ou soit ca ne fonctionne pas.

J'ai un pointeur de debut de chaine, un de fin (la taille du fichier), et le pointeur courant sur une ligne trouvé grace au strchr.

char *debut = machaine;
char *fin = debut + taillefichier;
char mem = buffer;

vous êtes sûr de pouvoir toujours mettre en mémoire le fichier ?

si la mémoire n'est pas un souci, faites une première passe et mettez dans un tableau la position des débuts de chaque ligne

sinon, il faut effectivement parcourir la mémoire en cherchant les passage à la ligne er en évitant d'aller avant debut et après fin.

un souci c'est si une ligne est plus longue que la largeur de votre écran. vous faites quoi dans ce cas?

Le fichier fait 721 lignes
Je n'ai pas encore gérer le dépassement écran pour l'instant, mon fichier ne dépasse pas 75 caractèeres, j'arrive juste à faire fonctionner la touche du bas, après je ferai la touche du haut, le PgDn, le PgUp et après je m'occupe des dépassement fin écran, début de ligne, fin de fichier etc ...

Le code flèche bas fonctionne.

  else if (next2 == 66) { //key down
            int linelen = nextLine(mem); //+1 is \n
            SERDEBUG.printf("dn linelen: %ld\r\n", linelen);
            if (mem + linelen  < fin ) {
              mem += linelen + 1;
              --Lig;
              if (Lig < 1) Lig = 1;
              drawtext(mem, sizearray, pdraw, Lig);
            }
          } 

la fonction nextLine c'est là ou il y a le strchr pour aller trouvé le pointeur de la prochaine ligne.
J'augmente donc le pointeur du début de fichier à la longueur trouvée grace à next line et j'ajoute 1
car je suppose que le pointeur est caler sur le '\n', donc je place le pointeur mem + 1 pour le prochain strchr.
mais pour pour la touche up rien ne fonctionne.

 if (next2 == 65)  {  //key up
            //linelen = prevLine(mem - 2) + 1;
            SERDEBUG.printf("up linelen: %ld\r\n", linelen);
            mem -= linelen+1;
            if (mem >= debut ) {
              SERDEBUG.println("mem > debut");             
              Lig++;
              if (Lig > totalLig) Lig = totalLig;
              drawtext(mem, sizearray, pdraw, Lig);
              linelen = prevLine(mem - 2);
            } else {
              SERDEBUG.println("mem <= debut");
              mem = debut;
              Lig = totalLig;
            }
          }

si vous travaillez avec tout en mémoire, je suppose que les fichiers sont petits et donc la parcourir est rapide.. perso donc je prendrai l'approche qui est de maintenir juste le N° de la première ligne affichée et le code recherche où se placer en fonction du N° de ligne.

par exemple essayez cela

u c'est UP
U c'est PAGE UP
d c'est DOWN
D c'est PAGE DOWN


const char fichier[] = R"--(.0|The quick brown fox jumps over the lazy dog
.1|The quick brown fox jumps over the lazy dog
.2|The quick brown fox jumps over the lazy dog
.3|The quick brown fox jumps over the lazy dog
.4|The quick brown fox jumps over the lazy dog
.5|The quick brown fox jumps over the lazy dog
.6|The quick brown fox jumps over the lazy dog
.7|The quick brown fox jumps over the lazy dog
.8|The quick brown fox jumps over the lazy dog
.9|The quick brown fox jumps over the lazy dog
10|The quick brown fox jumps over the lazy dog
11|The quick brown fox jumps over the lazy dog
12|The quick brown fox jumps over the lazy dog
13|The quick brown fox jumps over the lazy dog
14|The quick brown fox jumps over the lazy dog
15|The quick brown fox jumps over the lazy dog
16|The quick brown fox jumps over the lazy dog
17|The quick brown fox jumps over the lazy dog
18|The quick brown fox jumps over the lazy dog
19|The quick brown fox jumps over the lazy dog
20|The quick brown fox jumps over the lazy dog
21|The quick brown fox jumps over the lazy dog
22|The quick brown fox jumps over the lazy dog
23|The quick brown fox jumps over the lazy dog
24|The quick brown fox jumps over the lazy dog
25|The quick brown fox jumps over the lazy dog
26|The quick brown fox jumps over the lazy dog
27|The quick brown fox jumps over the lazy dog
28|The quick brown fox jumps over the lazy dog
29|The quick brown fox jumps over the lazy dog)--";

const size_t hauteurEcran = 5;
size_t ligneDebut = 0;

const char * debut = nullptr;
size_t nombreDeLignes = 0;

size_t compteLignes(const char * s) {
  size_t n = *s == '\0' ? 0 : 1;
  while (*s)
    if ((*s++ == '\n') && (*s != '\0')) n++;
  return n;
}

const char * pointeurDebutLigne(size_t numeroLigne) {
  const char * ptr = debut;
  for (size_t i = 0; i < numeroLigne; i++) {
    while (*ptr && *ptr++ != '\n');
  }
  return ptr;
}

void imprime(size_t numeroLigne) {
  char * ptr = pointeurDebutLigne(numeroLigne);

  for (size_t i = 0; i < hauteurEcran; i++) {
    while (*ptr && *ptr != '\n') Serial.write(*ptr++);
    Serial.write('\n');
    if (*ptr) ptr++;
  }
  Serial.println("----------");
}

void setup() {
  Serial.begin(115200); Serial.println();
  debut = fichier;
  nombreDeLignes = compteLignes(debut);
  if (nombreDeLignes == 0) {
    Serial.println("erreur, fichier vide");
    while (true) yield();
  }
  imprime(ligneDebut);
}

void loop() {
  switch (Serial.read()) {
    case 'u':   // up
      if (ligneDebut > 0) ligneDebut -= 1;;
      imprime(ligneDebut);
      break;
    case 'U':  // page up
      if (ligneDebut >= hauteurEcran) ligneDebut -= hauteurEcran;
      else ligneDebut = 0;
      imprime(ligneDebut);
      break;
    case 'd':   // down
      if (ligneDebut < nombreDeLignes - 1) ligneDebut += 1;;
      imprime(ligneDebut);
      break;
    case 'D':  // page down
      if (ligneDebut < nombreDeLignes - hauteurEcran) ligneDebut += hauteurEcran;
      else ligneDebut = nombreDeLignes - 1;
      imprime(ligneDebut);
      break;
  }
  delay(1);
}

Merci , je vais m'inspirer de ton code, et du coup aussi, les u U d D c'est mieux , plutôt que de tester le code escape, le code 91 et les touches 65 et 66.

Comme tu dit, je suis obligé de me limiter sur l'ESP32
la taille max c'est 1MB, il y a 16MB, mais arduino prend en compte seulement 4MB et les 4MB sont partager en deux pour la PSRAM, ce qui fait 2MB, je prend la moitié.

Déjà sur un fichier de 36k il y a 721 lignes, je me limiterai peut être à 64ko.

si vous voulez travailler hors mémoire et dans un fichier sans que ça rame, vous pouvez créer un fichier binaire parallèle à celui que vous voulez afficher et vous codez sur un uint32_t la position du début de de chacune des lignes lors d'une première passe. Comme chaque position fait le même nombre d'octets, trouver le début de la ligne N (en commençant à 0) revient à lire 4 octets dans ce fichier à la position 4.N et ça vous donne la position à laquelle vous placer (seek()) dans le ficher texte.

Oui c'est une solution aussi, je n'y avait pas pensé, mais au départ je vais étudier ton code pour la mise en oeuvre, car je cherche juste à afficher un fichier texte, parfois j'ai besoin de voir le contenu d'in fichier texte de la sdcard et ca permet de naviguer avec les touches. Merci.