Forum: Mikrocontroller und Digitale Elektronik Unicode Sonderzeichen und strlen() und sprintf() Funktionen


von D. S. (mrbean)


Lesenswert?

Hallo zusammen,

ich stehe vor dem Problem z.B. das Sonderzeichen "ě"  welches an Adresse 
0x011B liegt auf einem Grapfik Display darstellen zu müssen.
Die Firmware existiert bereits und läuft für Deutsch und Englisch. Soll 
nun also mit Sprachen und den entsprechenden Zeichen erweitert werden.
Bisher sind nur die unteren 255 Zeichen gepixelt und die ganze 
Adressierung ist nur 1 Byte groß. Das muss ich also eigentlich als 
erstes umbauen, um auch die nächsten Zeichen die oberhalb von 0xFF 
liegen anzeigen/adressieren zu können. Hier fangen die Probleme schon 
an. Derzeit sind meine Texte via "Beispiel" im Flash abgelegt. Sobald 
nun ein Zeichen mit zwei Byte Adressierung dazwischen kommt, habe ich 
ein Problem. Wie soll ich diesen Text im Speicher ablegen.
Bei ASCII Zeichen kümmert sich hierum ja der Compiler.

Das nächste Problem wären dann die Funktionen mit denen die Strings 
verarbeitet werden. Diese Funktionieren dann ja auch nichtmehr, da diese 
von 1-Byte pro Zeichen ausgehen. Ich habe hier schon recherchiert und 
multibyte String Funktionen gefunden.
Ich möchte aber trotzdem fragen wie ihr so ein Problem löst.

Adressiert ihr die Zeichen einfach in den noch freien Stellen der 
unteren 256 Adressen? und schaltet diese dann quasie händisch um, sodass 
die Adressierung im 1 Byte Raum bleiben kann?

Suche hier noch nach der einfachsten Möglichkeit...

Danke schonmal!

Gruß,
Bean

von B. S. (bestucki)


Lesenswert?

D. S. schrieb:
> Bisher sind nur die unteren 255 Zeichen gepixelt und die ganze
> Adressierung ist nur 1 Byte groß. [...]
> Bei ASCII Zeichen kümmert sich hierum ja der Compiler.

ASCII definiert nur 128 Zeichen (0...127).

Ich nehme jetzt mal an, dass es sich um C handelt:
Erstmal ist nicht klar, ob char signed oder unsigned ist, also ob die 
restlichen Zeichen die Werte 128...255 oder -128...-1 haben. Bei kleinen 
uCs mache ich das normalerweise so, dass ich diesen Wertebereich für die 
nötigen Sonderzeichen nutze. Beispiel:
1
#define DISPLAY_UE "\xAA" /* hier den richtigen Wert eintragen */
2
const char * const MeinText = "K" DISPLAY_UE "he machen M" DISPLAY_UE "he";
Etwas mühsam, funktioniert aber.

Du schreibst aber, dass du bereits 255 Zeichen definiert hast. Da sehe 
ich keinen Umweg um Multibyte Zeichen herum. uchar.h definiert wide 
string characters und wchar.h liefert die nötigen Funktionen dazu. Habe 
ich selbst noch nie benutzt, daher kann ich dir dabei keine Tipps geben.
http://www.cplusplus.com/reference/cuchar/
http://www.cplusplus.com/reference/cwchar/

: Bearbeitet durch User
von D. S. (mrbean)


Lesenswert?

Hallo Be Stucki,

danke für dein Feedback! Ja, es handelt sich um C. Der Wertebereich wäre 
positiv. Und Du hast natürlich recht, bisher ist nur der ASCII 
Zeichensatz allerdings von 20h bis FFh gepixelt. Also unsigned char.
Ok, das was Du beschreibst, wäre die andere Möglichkeit die ich sehe. 
Also die Sonderzeichen von z.B. 0x11B an die Adresse 0x80 zu pixeln. Das 
würde derzeit funktioniere, da hier noch keine Zeichen hinterlegt sind. 
Würde aber ein "um Mappen" der Zeichen zur Folge haben. Ich müsste dann 
in meinem String schreiben "\x80". Könnte also nichtmehr direkt das 
Zeichen "eingeben".
Ab Adresse 0x80 sind bis 0xFF sind schon einige Sonderzeichen enthalten. 
Aber eben nicht alle, die ich brauche. :-(

Gruß,
Bean

von Yalu X. (yalu) (Moderator)


Lesenswert?

Um Unicode- bzw. UTF-8-Zeichen zu verarbeiten, kannst du Wide-Characters
und -Strings  aus wchar.h verwenden und ggf. die Ausgabecodierung mit
setlocale aus locale.h festlegen.

Da du aber die Codierung für internationale Zeichen wohl nur innerhalb
deines Programms brauchst (ausgegeben werden ja nur die Pixel auf dem
Grafikdisplay), ist es einfacher und speichereffizienter, eine
8-Bit-Codierung wie ISO 8859-15 zu verwenden, sofern sie alle von dir
benötigten Zeichen enthält.

Wenn du deinen Editor oder C-Compiler entsprechend einstellst, kannst du
die Sonderzeichen in Stringliteralen auch direkt (ohne \x) in den
Quellcode eintippen.

Ich sehe gerade, dass ISO 8859-15 nicht geht, das dort das "ě" fehlt.
Wieviele unterschiedliche Zeichen brauchst du denn insgesamt? Sind es
mehr als 255?

von D. S. (mrbean)


Lesenswert?

Hallo Yalu,

:-) Genau das ist die Frage die mir derzeit auch keiner beantworten 
kann. Geplant sind 14 Sprachen. Welche Sonderzeichen ich hier alle 
brauche, habe ich noch nicht gezählt.
Irgendwo geistern aber auch noch Wünsche nach Asiatischen Schriftzeichen 
herum... Dann ist aus meiner Sicht eh Ende mit dem 1-Byte Adressraum.
Kennt ihr eine gute Seite, wo man sich die verschiedenen Zeichensätze 
anschauen kann?

Gruß,
Bean

von D. S. (mrbean)


Lesenswert?

Ich habe gerade mal verglichen. Derzeit gepixelt ist auf unserem Gerät 
der ISO/IEC 8859-15 Zeichensatz...

von Axel S. (a-za-z0-9)


Lesenswert?

D. S. schrieb:
> ich stehe vor dem Problem z.B. das Sonderzeichen "ě"  welches an Adresse
> 0x011B liegt

Und schon falsch. Zeichen "liegen" nicht an "Adressen". Zeichensätze 
definieren eine 1:1 Abbildung zwischen Zeichen und Zeichencodes. Z.B. 
hat dein oben genanntes Zeichen in Unicode den Code U+011B. Das gleiche 
Zeichen existiert aber auch in ISO Latin-2 mit dem Code 0xEC. Auch in 
Windows-1250 leigt dieses Zeichen auf 0xEC.

http://www.fileformat.info/info/unicode/char/011b/index.htm
http://www.fileformat.info/info/unicode/char/011b/charset_support.htm
https://de.wikipedia.org/wiki/ISO_8859-2

> Die Firmware existiert bereits und läuft für Deutsch und Englisch. Soll
> nun also mit Sprachen und den entsprechenden Zeichen erweitert werden.

Dann werdet euch als allererstes klar, welchen Zeichenvorrat ihr 
unterstützen wollt. Die Unicode BMP (basic multilingual plane aka die 
Unicode Codepoints U+0000 bis U+FFFF) ist zwar sicherlich am 
universellsten, aber alleine der Zeichensatz für ~65000 Zeichen braucht 
wahrscheinlich mehr Platz im ROM als eure ganze Anwendung. Wenn man nur 
eine Teilmenge der Zeichen braucht, dann bieten sich die ISO Latin-X 
Zeichensätze an, die nur 256 Zeichen enthalten.

> Derzeit sind meine Texte via "Beispiel" im Flash abgelegt. Sobald
> nun ein Zeichen mit zwei Byte Adressierung dazwischen kommt, habe ich
> ein Problem. Wie soll ich diesen Text im Speicher ablegen.
> Bei ASCII Zeichen kümmert sich hierum ja der Compiler.

UTF-8 ist extra dafür entwickelt worden, um einen gleitenden Übergang 
von 8-Bit Zeichencodierungen zu Unicode zu erleichtern. Latin-X verhält 
sich in jeder Hinsicht gleich wie ASCII.

> Das nächste Problem wären dann die Funktionen mit denen die Strings
> verarbeitet werden. Diese Funktionieren dann ja auch nichtmehr, da diese
> von 1-Byte pro Zeichen ausgehen. Ich habe hier schon recherchiert und
> multibyte String Funktionen gefunden.
> Ich möchte aber trotzdem fragen wie ihr so ein Problem löst.

Wenn das Zielsystem genug Resourcen hat, würde ich heute auf Unicode und 
UTF-8 Codierung setzen. Den Zeichensatz würde man dann aber "sparse" 
implementieren. Z.B. die ersten 256 Zeichen (entspricht Latin-1) 
komplett und von den Codepoints U+0100 bis U+FFFF nur den Teil, der 
wirklich gebraucht wird.

Latin-X oder Windows-XXXX ist eine gute Sparlösung, hat ja auch in Form 
der "Codepages" bei WinDOS jahrzehntelang gut funktioniert. Ob es paßt, 
hängt von der Anwendung ab.

von B. S. (bestucki)


Lesenswert?

Axel S. schrieb:
> Wenn das Zielsystem genug Resourcen hat, würde ich heute auf Unicode und
> UTF-8 Codierung setzen. Den Zeichensatz würde man dann aber "sparse"
> implementieren. Z.B. die ersten 256 Zeichen (entspricht Latin-1)
> komplett und von den Codepoints U+0100 bis U+FFFF nur den Teil, der
> wirklich gebraucht wird.

Würde ich so machen. Die Alternative wäre, für verschiedene Sprachen 
verschiedene Codepages zu verwenden. So könnten vielleicht die 8bit 
beibehalten werden. Das ganze mutiert aber zu unwartbarem Murks.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Es gibt noch eine Sparlösung, die mit 8 Bit pro Zeichen auskommt. Die 
kann man sich z.B. bei den HD44780 Displays anschauen. Diese Displays 
implementieren alle ASCII Zeichen auf den Positionen 0x20 bis 0x7F. In 
der oberen Hälfte von 0x80 bis 0xFF findet man dann z.B. Latin-15 oder 
irgendein schräges Komglomerat aus asiatischen Zeichen und europäischen 
Sonderzeichen. Darüber hinaus sind die Zeichen mit den Codes 0x00 bis 
0x1F ja Steuerzeichen, die separat ausgewertet werden und selber nicht 
auf dem Bildschirm dargestellt werden. Diese Positionen im Zeichensatz 
kann man auch noch für darstellbare Zeichen benutzen.

Der Nachteil an allen diesen Sparlösungen ist, daß nur für wenige davon 
die Strings in einem normalen Editor geschrieben (und angezeigt) werden 
können. Wenn man viel mit Strings hantieren muß oder sie z.b. aus dem PC 
übertragen will, braucht man dann Konvertierungsfunktionen.

von Michael B. (laberkopp)


Lesenswert?

D. S. schrieb:
> Wie soll ich diesen Text im Speicher ablegen.

Als UTF-8, das ist die standardisierte 8-bit Codierung für Unicode.

Und netterweise für ASCII, also die amerikanischen Zeichen, sogar 
identisch.

Du musst also beim Speichern und weiterleiten der Daten gar nichts 
anders machen.

Das Display bekommt dann eventuell mehrere Bytes für nur 1 
darzustellendes Zeichen. Dazu muss es erstens die Bytes zusammenfassen 
damit die Codezahl des Zeichens rauskommen.

Falls du Positionen auf Grund der Stringlänge berechnen willst, musst du 
mbslen für UTF8 verwenden, die liefert die Anzahl der Zeichen.

von D. S. (mrbean)


Lesenswert?

Hallo,

also auf jeden Fall haben wir Nordische Sprachen (z.B. Dänisch, 
Finnisch) und Osteuropäische (z.B. Tschechisch, Ungarisch).
Womit ich nach meinem jetzigen Verständniss mindestens zwei Zeichensätze 
bräuchte (Latin-1 und Latin-2).
In allen anderen genannten Zeichensätzen (UTF-8) sind hierfür nicht alle 
Zeichen enthalten. Sehe ich das richtig!?

Das "ě" wie es in der tschechischen Sprache gebraucht wird finde ich in 
dieser Tabelle:
http://www.utf8-chartable.de/
jedenfalls nicht.

Gruß und danke!

Bean

von B. S. (bestucki)


Lesenswert?

D. S. schrieb:
> Das "ě" wie es in der tschechischen Sprache gebraucht wird finde ich in
> dieser Tabelle:
> http://www.utf8-chartable.de/
> jedenfalls nicht.

Aber hier:
http://www.utf8-chartable.de/unicode-utf8-table.pl?start=256

UTF-8 hat weitaus mehr als 256 Zeichen. Schau mal ober auf der Seite bei 
"go to other block".

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

D. S. schrieb:
> Kennt ihr eine gute Seite, wo man sich die verschiedenen Zeichensätze
> anschauen kann?

Hier ist eine Liste der gebräuchlichsten Kodierungen:

  https://en.wikipedia.org/wiki/Character_encoding#Common_character_encodings

Auf den jeweils verlinkten Seiten findest du für die meisten davon auch
eine Zeichentabelle.

Aber so viel wird dir das nicht nützen, solange du nicht genau weißt,
welche Zeichen du tatsächlich brauchst. Und am Ende wird die
Entscheidung vielleicht doch auf UTF-8, -16 oder -32 hinauslaufen, weil
damit praktisch alle Zeichen darstellbar sind, die du jemals brauchen
wirst.

von D. S. (mrbean)


Lesenswert?

Ok, da mein "ě" bei UTF-8 im nächsten Block liegt, sehe ich das als 
UTF-16 an. Da ich hier ja noch das nächste Byte brauche.
Vielleicht liegt hier aber auch mein Denkfehler.
Reicht es aus, wenn ich meinen Compiler und meine IDE auf UTF-8 stelle? 
Erkennt "adressiert" er das Zeichen "ě" dann richtig mit 0x011B!?
Dann müsste ich meinen Zeichensatz aber trotzdem auf 2 Byte umbauen, da 
ich ja die 0x01 aus dem HighByte auch irgendwie verarbeiten muss.
Sorry für meinen dumme Fragerei, aber ich sehe die praktische Lösung 
(wie komme ich zu C-Sourcecode der mir das "ě" anzeigt) noch nicht...

Gruß,
Bean

von Axel S. (a-za-z0-9)


Lesenswert?

D. S. schrieb:
> ... auf jeden Fall haben wir Nordische Sprachen (z.B. Dänisch,
> Finnisch) und Osteuropäische (z.B. Tschechisch, Ungarisch).
> Womit ich nach meinem jetzigen Verständniss mindestens zwei Zeichensätze
> bräuchte (Latin-1 und Latin-2).

Schau auch mal Windows-1250 bzw. 1252 an

> In allen anderen genannten Zeichensätzen (UTF-8) sind hierfür nicht alle
> Zeichen enthalten. Sehe ich das richtig!?

Das siehst du falsch. UTF-8 ist eine Transportcodierung für Unicode.
Da gibt es jedes Zeichen.

> Das "ě" wie es in der tschechischen Sprache gebraucht wird finde ich in
> dieser Tabelle: http://www.utf8-chartable.de/ jedenfalls nicht.

Die Tabelle ist ja auch nur für die ersten 256 Zeichen von Unicode.
Aka Latin-1.

Einen besseren Link hatte ich dir oben ja schon gegeben:

http://www.fileformat.info/info/unicode/char/011b/charset_support.htm

das listet alle Zeichensätze bzw. Codepages auf, die das gewünschte 
Zeichen enthalten.

Etwas allgemeiner: dein Zeichen bei 
http://www.fileformat.info/info/unicode/char/search.htm suchen. Auf der 
Seite für das Zeichen unter "Encodings" auf "More" klicken.

von Der Andere (Gast)


Lesenswert?

D. S. schrieb:
> Ok, da mein "ě" bei UTF-8 im nächsten Block liegt, sehe ich das als
> UTF-16 an.

Wie du das ansiehst ist ziemlich egal, UTF-16 ist ein 16Bit (2Byte) 
Format, UTF-8 ist eine Codierung variabler Länge, die für die üblichen 
westlichen Zeichen für die meisten Zeichen mit nur einem Byte auskommt.

von Fritz G. (fritzg)


Lesenswert?

Dazu kommt noch folgendes:
Du kannst zwar mit den verschiedenen ISO8859 Zeichensätze verschiedene 
Sprachen darstellen, in dem du den entsprechenden Zeichensatz bei dir 
auswählst. Du kannst aber nicht auf einer Seite mehrere Sprachen 
mischen. Das geht in der PC Welt und auf Webseiten prinzipiell nicht.
Deswegen hat man Unicode eingeführt. Damit kannst du alle, wirklich alle 
Zeichen der Welt auf einmal darstellen. Passt natürlich nicht mehr in 
ein Byte, so dass die "exotischen" Zeichen bei UTF-8 zwei oder auch mehr 
Bytes verwenden.

Ich habe ein ähnliches Problem mit einem 16x2 Textdisplay, welches nicht 
einmal alle Umlaute kann. Da der Text, der dort u.a. angezeigt wird vom 
User in der App bestimmt wird, müssen alle nicht-ASCII Zeichen 
umgewandelt werden. Sieht zwar nicht schön aus, bewahrt aber davor, dass 
das Display nicht darstellbare Bytes bzw. strlen falsche Länge bekommt.

Für dich wäre es eine Möglichkeit, dir eine Konvertierungsfunktion zu 
schreiben, die den Zeichensatz deines Editors in dein eigenes Mapping 
umschreibt. In dieses Mapping machst du ab 128 alle Zeichen rein, die du 
brauchst.

von D. S. (mrbean)


Lesenswert?

>UTF-8 ist eine Codierung variabler Länge, die für die üblichen
>westlichen Zeichen für die meisten Zeichen mit nur einem Byte auskommt.

Genau, allerdings nicht für mein "ě".
Da bin ich nämlich bei 0x011B.
Ich glaube das mit den verschiedenen Zeichensätzen habe ich jetzt 
verinnerlicht.

>Für dich wäre es eine Möglichkeit, dir eine Konvertierungsfunktion zu
>schreiben, die den Zeichensatz deines Editors in dein eigenes Mapping
>umschreibt. In dieses Mapping machst du ab 128 alle Zeichen rein, die du
>brauchst.

Geht schon ziemlich in die Richtung die ich auch als Lösung im Kopf 
habe.

Gruß,
Bean

von Axel S. (a-za-z0-9)


Lesenswert?

D. S. schrieb:
> Ok, da mein "ě" bei UTF-8 im nächsten Block liegt, sehe ich das als
> UTF-16 an.

Nein

UTF-8 hat keine "Blöcke". Unicode hat Blöcke. Ein Zeichencode aus dem 
Unicode-Universum heißt Codepoint und wird U+xxxx geschrieben, wobei 
xxxx eine Hexadezimalzahl ist. Es gibt keine Längenbegrenzung für 
diese Hexadezimalzahl. In den ersten Versionen von Unicode glaubte man 
noch, 4 Stellen (U+0000 bis U+FFFF) würden reichen. Mittlerweile sind 
aber deutlich mehr als 65536 Zeichen (U+000000 bis U+10FFFF) in Unicode 
standardisiert so daß 2 Byte pro Zeichen nicht mehr reichen, um alle 
Zeichen aus Unicode darzustellen.

UTF-8 ist eine Codierung, die Unicode-Zeichen in einen Strom von Bytes 
codiert und dabei die Eigenschaft behält, daß 0x00 in keinem Zeichen 
vorkommt, folglich weiterhin als Endekennzeichen verwendet werden kann.

UTF-16 ist das gleiche für einen Strom von 16-Bit Worten.

Sowohl in UTF-8 als auch in UTF-16 kann ein einzelnes Zeichen in einer 
Variablen Anzahl von Bytes (UTF-8: 1..5 Bytes) bzw. Worten (UTF-16: 1 
oder 2 Worte) codiert werden.

: Bearbeitet durch User
von Jay (Gast)


Lesenswert?

D. S. schrieb:
> Ok, da mein "ě" bei UTF-8 im nächsten Block liegt, sehe ich das
> als
> UTF-16 an. Da ich hier ja noch das nächste Byte brauche.
> Vielleicht liegt hier aber auch mein Denkfehler.

Ja, du machst immer noch nicht die Trennung zwischen der internen 
Speicherung (Kodierung) eines Wertes und dem Wert selber.

Die UTF-x Kodierungs-Formate (in voller Schönheit UTF = Universal 
Character Set Transformation Format) beschreiben wie ein Wert im 
Speicher abgelegt wird oder übertragen wird. Also das Bitmuster.

Zum Beispiel ist UTF-8 dabei für kleine Werte kompakt (1 Byte für Werte 
0 - 0x7f), kann aber alle Werte bis 0x1fffff kodieren. Für 0x1ffff 
braucht es allerdings 4 Bytes (eine unkodierte Speicherung von 0x1ffff 
würde nur 3 Bytes belegen und einige MSBs wären dabei noch unbenutzt).

UTF-8 ist für große Werte also weniger effizient und hat den Nachteil, 
dass es ein Format mit variabler Länge ist, je nach Wert werden mal mehr 
mal weniger Bytes zur Speicherung verwendet.

Die Werte die man mit UTF-8 oder genauso mit UTF-16 speichern kann 
entsprechen den selben Zeichen im Universal Character Set (UCT). Die 
Werte werden Code Points genannt, und sind bei UTF-8 eben nicht 1:1 die 
Bytes im Speicher.

Jedem Code Point ist ein Glyphe zugeordnet. Der Glyphe ist die sichtbare 
Repräsentation eines Zeichens.

> Reicht es aus, wenn ich meinen Compiler und meine IDE auf UTF-8 stelle?

Nein, dann kannst du deinen Source-Code in UTF-8 speichern und der 
Compiler würde bei einem String Literal mit ě die entsprechende UTF-8 
Bytefolge einfügen.

> Erkennt "adressiert" er das Zeichen "ě" dann richtig mit 0x011B!?

Nein, warum sollte er? Die UTF-8 Kodierung für den Code-Point 0x011b 
sind  zwei Bytes 0xc4 0x9b. Die würdest du in deinem Sourcecode-File, 
bzw. in deinem Compiler-Output sehen.

> Dann müsste ich meinen Zeichensatz aber trotzdem auf 2 Byte umbauen, da
> ich ja die 0x01 aus dem HighByte auch irgendwie verarbeiten muss.

Ehm ... Du müsstest die Abbildung von Code Points auf Glyph-Daten so 
umbauen, dass auch die Daten für Zeichen mit Code Points > 255 gefunden 
werden.

Da es offensichtlich keinen Sinn macht dafür ein Array (Lookup-Table) 
mit zum Beispiel 2097152 Einträgen zu verwenden (Wertebereich von 
UTF-8), wirst du dafür eine andere Technik benötigen. Zum Beispiel 
Binäres Suchen in einer sortierten Liste von unterstützten Code Points 
oder eine Hashtable (aka Map). Natürlich kann man bei UTF-8 ein bisschen 
optimieren und für die ersten 128 Code Points eine kleine Lookup-Table 
verwenden. Für die weiteren dann die sortierte Liste oder Map.

> Sorry für meinen dumme Fragerei, aber ich sehe die praktische Lösung
> (wie komme ich zu C-Sourcecode der mir das "ě" anzeigt) noch nicht...

Speicher-Kodierung auswählen, zum Beispiel UTF-8. Dann ist der Weg vom 
Inhalt im Speicher:
1
UTF-x dekodieren -> Code Point -> Glyph Daten -> Display

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.