Frage zu CharArray strlen() und Umlaut

Code: (ohne Umlaut)

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println(F("Start..."));
  const char tak[] = "zurueck";
  Serial.println(strlen(tak));
  for (uint8_t i = 0; i < strlen(tak); i++)
  {
    Serial.println(tak[i], BIN);
  }
}

void loop()
{
}

Ausgabe:

Start...
7
1111010
1110101
1110010
1110101
1100101
1100011
1101011

Verwendeter Speicher (MEGA / IDE 1.8.15)

Der Sketch verwendet 2208 Bytes 
Globale Variablen verwenden 196 Bytes

Code: (mit Umlaut)

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println(F("Start..."));
  const char tak[] = "zurück";
  Serial.println(strlen(tak));
  for (uint8_t i = 0; i < strlen(tak); i++)
  {
    Serial.println(tak[i], BIN);
  }
}
void loop()
{
}

Ausgabe:

Start...
7
1111010
1110101
1110010
11111111111111111111111111000011
11111111111111111111111110111100
1100011
1101011

Verwendeter Speicher siehe oben.

strlen() ergibt auch 7.
Ich will die Anzahl der dargestellten Zeichen.
Welches C++ Statement oder welches Konstrukt bringt mich dahin, das ich da eine 6 rausbekomme?
Muss ich das tatsächlich händisch machen, indem ich das CharArray durchgehe und nach Umlauten suche?

Ich verzichte jetzt auf Umlaute, ich will es nur wissen.

das ü ist halt in UTF-8 ein zwei byte Zeichen.

Wird dir nichts anderes übrigbleiben, als dass du deine Zeichen auf UTF-8 prüfst wenns notwendig ist.
Siehst an den ersten vier Bits je Zeichen wie viele Bytes dein Zeichen braucht.

Ich hatte die Hoffnung, das an Stelle von strlen() mir irgendwas das abnimmt. :cry:

Ok.
Dann ist es so....

aber ich glaub das hast schon gesehen ...

wobei ich den Rollover noch nicht checke:

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println(F("Start..."));
  const char tak[] = "zurück";
  Serial.println(strlen(tak));
  for (uint8_t i = 0; i < strlen(tak); i++)
  {
    Serial.print(i);
    Serial.print(" ");
    Serial.print(tak[i]);
    Serial.print(" ");
    Serial.println(tak[i], HEX);
  }
}
void loop()
{}

Start...
7
0 z 7A
1 u 75
2 r 72
3 ⸮ FFFFFFC3
4 ⸮ FFFFFFBC
5 c 63
6 k 6B

1 Like

Und vieles mehr. Das Suchmaschinendingens ist noch offen - da reichte

https://www.google.com/search?channel=fs&client=ubuntu&q=strlen%28%29+umlaute

Nach den php'lern waren dann auch die einen oder anderen MS Dingens bei.
Tenor ist - richtig - das der dargestellte Buchstabe eben nicht ein uint8_t / char ist.
Und nu war ich der Hoffnung, das jemand im C++ Satz etwas gefunden hat, was ein .len richtig ausgibt...

Es ist wie es ist...

hab das:

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println(F("Start..."));
  const char tak[] = "zurück";
  Serial.println(strlen(tak));
  for (uint8_t i = 0; i < strlen(tak); i++)
  {
    Serial.print(i);
    Serial.print(" ");
    Serial.print(tak[i]);
    Serial.print(" ");
    Serial.println(tak[i], HEX);
  }

  Serial.println("aber...");
  Serial.println(utf8_strlen(tak));
}
void loop()
{}

int utf8_strlen(const char * str) // http://www.zedwood.com/article/cpp-utf8-strlen-function
{
  int c, i, ix, q;
  for (q = 0, i = 0, ix = strlen(str); i < ix; i++, q++)
  {
    c = (unsigned char) str[i];
    if      (c >= 0   && c <= 127) i += 0;
    else if ((c & 0xE0) == 0xC0) i += 1;
    else if ((c & 0xF0) == 0xE0) i += 2;
    else if ((c & 0xF8) == 0xF0) i += 3;
    //else if (($c & 0xFC) == 0xF8) i+=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
    //else if (($c & 0xFE) == 0xFC) i+=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
    else return 0;//invalid utf8
  }
  return q;
}

bringt

Start...
7
0 z 7A
1 u 75
2 r 72
3 ⸮ FFFFFFC3
4 ⸮ FFFFFFBC
5 c 63
6 k 6B
aber...
6

2 Likes

Ach schau!
Lesezeichen gesetzt.
DANKE! :joy:

ja, aber das ver-Arduino'st eh noch oder? weil diese For schleife, das erste If ... ich zweifle grad an mir selber

Da staunt der Laie und der Fachmann wundert sich ....

Ich hab das hier auch auf - und find das schon tricky.
Den find ich super:

if      (c >= 0   && c <= 127) i += 0;

Muss man drauf kommen.

OT:
..Blaulicht und Sirenen vor der Tür. Man liest sich.

für den Herrn Inspektor an der Tür eine Variante:

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println(F("Start..."));
  const char tak[] = "zurück";
  Serial.println(strlen(tak));
  for (uint8_t i = 0; i < strlen(tak); i++)
  {
    Serial.print(i);
    Serial.print(" ");
    Serial.print(tak[i]);
    Serial.print(" ");
    Serial.println(tak[i], HEX);
  }

  Serial.println("aber...");
  Serial.println(utf8_strlen(tak));
  Serial.println(utf8_strlen4dummies(tak));
}
void loop()
{}

int utf8_strlen(const char * str) // http://www.zedwood.com/article/cpp-utf8-strlen-function
{
  int c, i, ix, q;
  for (q = 0, i = 0, ix = strlen(str); i < ix; i++, q++)
  {
    c = (unsigned char) str[i];
    if      (c >= 0   && c <= 127) i += 0;
    else if ((c & 0xE0) == 0xC0) i += 1;
    else if ((c & 0xF0) == 0xE0) i += 2;
    else if ((c & 0xF8) == 0xF0) i += 3;
    //else if (($c & 0xFC) == 0xF8) i+=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
    //else if (($c & 0xFE) == 0xFC) i+=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
    else return 0;//invalid utf8
  }
  return q;
}

int utf8_strlen4dummies(const char * str)
{
  byte c = 0;
  byte r = 0; // result
  for (size_t i = 0; i < strlen(str); i++)
  {
    c = (unsigned char) str[i];
    if (c <= 127) r++;
    else if ((c & 0xE0) == 0xC0) 
    {
      i += 1;
      r++;
    }
    else if ((c & 0xF0) == 0xE0) 
    {
      i += 2;
      r++;
    }
    else if ((c & 0xF8) == 0xF0) 
    {
      i += 3;
      r++;
    }
  }
  return r;
}

Wenn man mal angefixxt ist :wink:

Nochmal Danke.

1 Like

angefixt dürfte stimmen.

Die V3 braucht etwas weniger Speicher als das Original und gibt nun auch 0 bei "kaputten" UTF-8 strings retour. Wenn man das return 0 nicht braucht, ist es noch mal etwas kleiner.

Daher gibts jetzt auch "kaputte" Testdaten und der Teststring enthält nun 1, 2, 3 und 4 Byte Zeichen

// https://forum.arduino.cc/t/frage-zu-chararray-strlen-und-umlaut/897224/

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println(F("Start..."));
  //const char tak[] = "zurück";
  const char tak[] = "y䮀𝄞";     // examples in wikipedia.de
  //const char tak[]  {0x79, 0xC3, 0xA4, 0xAE, 0xE2, 0x82, 0xAC, 0xF0, 0x9D, 0x84, 0x9E}; // "missing" start byte for ®
  
  for (uint8_t i = 0; i < strlen(tak); i++)
  {
    Serial.print(i);
    Serial.print(" ");
    Serial.print(tak[i]);
    Serial.print(" ");
    Serial.println((byte)tak[i], HEX);  // you need this explicit cast from char to byte, otherwise print BIN will break on Arduino...
  }
  Serial.print (F("strlen       =")); Serial.println(strlen(tak));
  Serial.print (F("utf8_strlen  =")); Serial.println(utf8_strlen(tak));      // 2104/202
  Serial.print (F("utf8_strlenV2=")); Serial.println(utf8_strlenV2(tak));    // 2096/202
  Serial.print (F("utf8_strlenV3=")); Serial.println(utf8_strlenV3(tak));    // 2096/202
}

void loop()
{}

int utf8_strlen(const char * str) // http://www.zedwood.com/article/cpp-utf8-strlen-function
{
  int c, i, ix, q;
  for (q = 0, i = 0, ix = strlen(str); i < ix; i++, q++)
  {
    c = (unsigned char) str[i];
    if      (c >= 0   && c <= 127) i += 0;
    else if ((c & 0xE0) == 0xC0) i += 1;
    else if ((c & 0xF0) == 0xE0) i += 2;
    else if ((c & 0xF8) == 0xF0) i += 3;
    //else if (($c & 0xFC) == 0xF8) i+=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
    //else if (($c & 0xFE) == 0xFC) i+=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
    else return 0;//invalid utf8
  }
  return q;
}


/*
    calculates the true character length of a UTF-8 c string
*/
int utf8_strlenV2(const char * str)
{
  byte c = 0;
  byte r = 0; // result
  for (size_t i = 0; i < strlen(str); i++)
  {
    c = (unsigned char) str[i];
    if (c <= 127) r++;
    else if ((c & 0xE0) == 0xC0)    // 0b11100000   0b11000000
    {
      i += 1;
      r++;
    }
    else if ((c & 0xF0) == 0xE0)    //  0b11110000   0b11100000
    {
      i += 2;
      r++;
    }
    else if ((c & 0xF8) == 0xF0)   //  0b11111000   0b11110000
    {
      i += 3;
      r++;
    }
  }
  return r;
}


/*
    calculates the true character length of a UTF-8 c string
*/
size_t utf8_strlenV3(const char * str)
{
  byte c = 0;
  byte r = 0; // result
  for (size_t i = 0; i < strlen(str); i++)
  {
    c = (unsigned char) str[i];
    if (c <= 127) r++;
    else
    {
      switch (c & 0b11111000)
      {
        case 0b11000000 :  // one sequence byte
          i += 1;
          r++;
          break;
        case 0b11100000 :  // two sequence bytes
          i += 2;
          r++;
          break;
        case 0b11110000 :  // three sequence bytes
          i += 3;
          r++;
          break;
        default :          // invalid RCF3629, 
          return 0;        // void result and exit - if you need "valid" characters, comment this line
          break;
      }
    }
  }
  return r;
}
Start...
0 y 79
1 ⸮ C3
2 ⸮ A4
3 ⸮ C2
4 ⸮ AE
5 ⸮ E2
6 ⸮ 82
7 ⸮ AC
8 ⸮ F0
9 ⸮ 9D
10 ⸮ 84
11 ⸮ 9E
strlen       =12
utf8_strlen  =5
utf8_strlenV2=5
utf8_strlenV3=5

lustig auch, der explizite Cast auf byte (damit die FFFFFF weg sind), spart sogar etwas flash.

edit...
ein Fass ohne Boden!
Natürlich findet Google kürzere Lösungen als der kleine noiasca da zusammenkleistert...
ohne Failsafe:

/*
    calculates the true character length of a UTF-8 c string
    https://stackoverflow.com/questions/4063146/getting-the-actual-length-of-a-utf-8-encoded-stdstring
*/
size_t utf8_strlenV4(const char * str)
{
  int len = 0;
  while (*str) len += (*str++ & 0xc0) != 0x80;
  return len;
}
2 Likes

Du bist verrückt :slight_smile:
Vielen lieben Dank für das ausarbeiten.
Gibt es eigentlich irgendwo einen Thread in dem "die spannendsten Lösungen" vorgeschlagen und gesammelt werden? Irgendwie fehlt sowas noch....

1 Like