Umlaute in C



  • Hi(gh)!

    Und weiter geht es mit ERLENKÖTTER, Helmut: C - Programmieren von Anfang an... auf Seite 60 ist in den erklärenden Anmerkungen zum Programmskript "bspl0019.c" zu lesen, dass die (in ctype.h enthaltenen) Funktionen toupper() und tolower() mit Umlauten (das große ẞ gab es damals noch nicht) nicht funktionieren... das fand ich interessant und wollte es dann gleich mal ausprobieren:

    #include <stdio.h>
    #include <ctype.h>
    
    main()
    {
      char eingabe;
      printf("\nGeben Sie ein deutsches Sonderzeichen als Großbuchstabe ein (Umlaut oder ẞ): ");
      scanf("%c",&eingabe);
      if ( !(eingabe =='Ä' || eingabe=='Ö' || eingabe=='Ü' || eingabe=='ẞ'))
        printf("Dies war leider kein passendes Zeichen!\n");
      else
      {  
        eingabe=tolower(eingabe);
        printf("\nIhre Eingabe als Kleinbuchstabe: %c\n",eingabe);
      }
    }
    

    Beim Komplieren (im C90-Modus) wirft gcc vier Warnmeldungen (jeweils mit unterschlängeltem Sonderzeichen):

    warning: multi-character character constant [-Wmultichar]
    12 | if ( !(eingabe =='Ä' || eingabe=='Ö' || eingabe=='Ü' || eingabe=='ẞ'))

    und wenn ich das Programm dann starte, wird die if-Bedingung auch bei Eingabe von Ä, Ö, Ü oder ẞ nie falsch!

    Multi-character constant: klar, das sind Zeichen, die anders als Standard-ASCII mit mehr als einem Byte kodiert sind... was hat es mit dem Kommandozeilen-Switch -Wmultichar auf sich?

    Bis bald im Khyberspace!

    Yadgar



  • Die Textkodierung wird durch die C Norm nicht festgelegt, da so ziemlich jede Plattform eine eigene Kodierung nutzte. IBMs Mainframes nutzen die EBCDIC Kodierung andere nutzten ASCII. ASCII definiert aber nur Zeichen für US Englisch in den unteren 7Bit eines Bytes, so dass es verschiedene DOS Codepages gibt, für die Kodierung der jeweiligen Sonderzeichen der anderen Sprachen innerhalb dieser unteren 7Bit. ISO hat dann 8Bit Kodierungen für die wichtigsten Sprachen definiert. UNIX nutzt als Standard ISO-Latin-1 8859-1, was das Umstellen der Codepages für „den Westen“ ersparte. ISO 8859-15 (bzw. ISO Latin-9) ist die Variante mit dem Euro-Zeichen. Windows nutzte hingegen eine daran angelehnte Kodierung, die aber nicht exakt gleich war.

    • Du musst festlegen für welche Plattform Du das Programm schreiben willst.
    • Die Textdatei muss entsprechend kodiert abgespeichert werden.
    • Wahrscheinlich musst Du auf der Kommandozeile die Locales anpassen, so dass das auch korrekt interpretiert wird.

    Neue Texteditoren verarbeiten Text als Unicode und speichern ihn meist als UTF-8 ab. Hier solltest Du darauf achten, dass das in der passenden Kodierung geschieht.



  • Der von Dir hier gepostete Code enthält das große ẞ, was in ISO-Latin-1 nicht kodierbar ist.



  • @Yadgar
    Vorausgesetzt du arbeitest in einer UTF-8-Umgebung: "Ä" ist entweder die UTF-8-Sequenz 0xC3 0x84 (Unicode: Ä - LATIN CAPITAL LETTER A WITH DIAERESIS) oder 0x41 0xCC 0x88 (Unicode: A + ◌̈ - LATIN CAPITAL LETTER A + COMBINING DIAERESIS).

    Wenn man einem char-Literal wie in diesem Fall mehrere Zeichen zuweist (z.B. char c = 'ABC';) dann gibt es erstens diese Warnung und was für ein char daraus letztendlich gemacht wird, ist soweit ich weiß "implementation-defined".

    Meines Wissens dürfte z.B. ein GCC die beiden Zeichen als int interpretieren, also entweder der int 0xc384 oder 0x41cc88. Dieser wird dann bei der Zuweisung in einen char konvertiert, d.h. die führenden Bits werden einfach "abgeschnitten". Also entweder 0x84 oder 0x88.

    Im Windows-1252 oder ISO-8859-1-Zeichensatz dürften das die Zeichen „ und ˆ sein. Die kannst du ja mal ausprobieren. Aber ohne Garantie, weil eben "implementation defined" und auch noch stark abhängig vom Zeichensatz der Konsole und der .c-Datei 😉



  • @Finnegan sagte in Umlaute in C:

    Wenn man einem char-Literal wie in diesem Fall mehrere Zeichen zuweist (z.B. char c = 'ABC';) dann gibt es erstens diese Warnung und was für ein char daraus letztendlich gemacht wird, ist soweit ich weiß "implementation-defined".

    Der Grund ist, dass es damals keinerlei Festlegung gab wie groß ein char sein musste. Mein persönliches historisches Lieblingsbeispiel ist UNICOS. Da ist ein char 64Bit groß. Die Single UNIX Spec definiert später ein charals immer 8Bit groß. Aber das sind Festlegungen außerhalb der ISO C Norm.



  • @john-0 sagte in Umlaute in C:

    Der Grund ist, dass es damals keinerlei Festlegung gab wie groß ein char sein musste. Mein persönliches historisches Lieblingsbeispiel ist UNICOS. Da ist ein char 64Bit groß. Die Single UNIX Spec definiert später ein charals immer 8Bit groß. Aber das sind Festlegungen außerhalb der ISO C Norm.

    Das spielt sicher auch mit rein, aber sollte dann z.B. 'A' nicht eher für einen 64-bit Integer stehen, z.B. 0x0000000000000041? Nach dem Schema für char-Literale mit mehreren Zeichen müsste da auch der Code für entweder das erste oder das letzte Zeichen rauskommen (denke auch noch von Endianness abhängig).

    Ansonsten: Ich hab ich in meinem Beitrag auch noch eine "UTF-8-Umgebung" erwähnt. Mir kommt grad noch in den Sinn, dass z.B. unter Windows die Konsole primär mit UTF-16 arbeitet. Das macht es nochmal etwas komplizierter abzuschätzen, was bei dem Programm wirklich passiert. Eventuell kann man nämlich wirklich kein Zeichen eingeben, bei dem die Bedingung greift (keinen Nerv grad zu überlegen, mit welcher Eingabe man da "tricksen" könnte. Das da oben reicht mir für heute 😉 ).



  • @john-0

    Ich programmiere C unter Linux auf der Konsole, mein Texteditor ist kwrite, der wiederum ist auf UTF-8 eingestellt (mit ISO 8859-15 als Ausweich-Kodierung). Deutsche Sonderzeichen in printf-Ausgaben werden problemlos angezeigt (ob auch das große ẞ habe ich bis jetzt noch nicht ausprobiert).

    Wo lege ich denn die Plattform für das Programm fest? Und wie passe ich die Locales an?



  • @Finnegan sagte in Umlaute in C:

    Das spielt sicher auch mit rein, aber sollte dann z.B. 'A' nicht eher für einen 64-bit Integer stehen, z.B. 0x0000000000000041? Nach dem Schema für char-Literale mit mehreren Zeichen müsste da auch der Code für entweder das erste oder das letzte Zeichen rauskommen (denke auch noch von Endianness abhängig).

    Damals war alles etwas wild. Ich kann Dir nicht sagen wie es exakt war, da ich UNICOS nie selbst genutzt habe.

    Heute funktioniert das unter UNIX/Linux wie folgt.
    Der Sourcecode wird in UTF-8 eingelesen, daher hat man kein Byteorder-Problem. Zeichenkonstanten wie z.B. 'a' werden als 8Bit Zeichen interpretiert. Das kann mit UTF-8 Probleme geben, weil UTF-8 ein variables Byte-Encoding ist, und wenn das dann nicht passt, wird abgeschnitten. Schreibst Du hingegen L'a' in den Sourcecode wird das als wchar_t (32bit) interpretiert und vom Compiler in die passende Byteorder der Zielplattform gebracht.

    Bei Strings sieht die Sache etwas anders aus. Schreibt man da "Ein String mit Umlauten ÄÖÜ", hängt es vom Sourcecode und der locale Einstellung ab, wie das interpretiert wird. Der Voreinstellung ist aktuell, dass der Sourcecode als UTF-8 vorliegen muss, es als UTF-8 interpretiert wird und auch so ins Programm kommt. Früher wurde das als ISO-Latin-1 im Editor abgelegt und vom Compiler auch so ins Programm kodiert. Schreibt man hingegen L"Ein String mit Umlauten ÄÖÜ" wird das zu einem wchar_t String vom Compiler übersetzt (der Source sollte als UTF-8 vorliegen, sonst kommt es zu Fehler bei der Interpretation der Zeichen), und in der Zielplattform Byteorder im Code abgelegt.

    Nimmt man wie hier im Beispiel printf und scanf interpretiert Linux das nun als UTF-8, das Problem hierbei ist, dass die Umlaute als Multibyte vorliegen und so das alte Programm nicht mehr richtig funktioniert. Weder die Umlaute für den Vergleich funktionieren, noch die Eingabe als ein Zeichen funktioniert.

    Ansonsten: Ich hab ich in meinem Beitrag auch noch eine "UTF-8-Umgebung" erwähnt. Mir kommt grad noch in den Sinn, dass z.B. unter Windows die Konsole primär mit UTF-16 arbeitet. Das macht es nochmal etwas komplizierter abzuschätzen, was bei dem Programm wirklich passiert. Eventuell kann man nämlich wirklich kein Zeichen eingeben, bei dem die Bedingung greift (keinen Nerv grad zu überlegen, mit welcher Eingabe man da "tricksen" könnte. Das da oben reicht mir für heute 😉 ).

    Windows nutzt generell UTF-16 für Unicode. Nur mit WSL gibt es die UNIX/Linux Variante.


Anmelden zum Antworten