Probleme mit Umlauten



  • @germanveryhard sagte in Probleme mit Umlauten:

    ig

    Wie kommst du eigentlich auf die Zahlen 142, 132, 153,...?

    #include <iostream>
    
    using namespace std;
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("input.txt", "r", stdin);
    	freopen("output.txt", "w", stdout);
    #endif
    	ios_base::sync_with_stdio(false);
    
    	const unsigned char AE = static_cast<unsigned char>(142);	// Wie kommst du auf diese Zahlen?
    	const unsigned char ae = static_cast<unsigned char>(132);
    	const unsigned char OE = static_cast<unsigned char>(153);
    	const unsigned char oe = static_cast<unsigned char>(148);
    	const unsigned char UE = static_cast<unsigned char>(154);
    	const unsigned char ue = static_cast<unsigned char>(129);
    	string s = "Übung";
    
    	if (s[0] == UE){
    		cout << "LALA";
    	}
    	cout << "Tüpfelhyäneöhrchenstraße\n";
    	printf("Coding of Ü: %i\n", static_cast<unsigned char>('Ü'));	
    	cout << "Coding of UE: " << static_cast<int>(UE) << "\n";
    	printf("Coding of s[0]: %i\n", s[0]);
    	printf("Coding of Ü (signed): %i\n", static_cast<char>('Ü'));
    
    	cout << AE << " " << OE << " " << UE << " " << ae << " " << oe << " " << ue;
    	return 0;
    }
    

    Wenn ich das Programm ausführe, so erhalte ich folgende Ausgabe:

    Tüpfelhyäneöhrchenstraße
    Coding of Ü: 220
    Coding of UE: 154
    Coding of s[0]: -36
    Coding of Ü (signed): -36
    Ž ™ š „ ” 
    

    Du codierst den Buchstaben Ü mit der Zahl 154, der Compiler möchte hier aber eine 220 sehen. DIes entspricht der Codierung mittels Codepage 1252. Dort ist das Zeichen hexdezimal mit 0xDC codiert. Wandele ich diese Zahl in dezimal um, so erhalte ich 220. Da bei mir char als signed interpretiert wird, läuft bei mir die Zahl 220 über, d.h. ich muss 256 = (2**8) abziehen: 220 - 256 = -36, uns siehe da, diese Zahl entspricht s[0].

    So sollte es funktionieren:

    #include <iostream>
    
    using namespace std;
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("input.txt", "r", stdin);
    	freopen("output.txt", "w", stdout);
    #endif
    	ios_base::sync_with_stdio(false);
    
    	const char AE = 'Ä';
    	const char ae = 'ä';
    	const char OE = 'Ö';
    	const char oe = 'ö';
    	const char UE = 'Ü';
    	const char ue = 'ü';
    	string s = "Übung";
    
    	if (s[0] == UE){
    		cout << "LALA";
    	}
    	/*cout << "Tüpfelhyäneöhrchenstraße\n";
    	printf("Coding of Ü: %i\n", static_cast<unsigned char>('Ü'));	
    	cout << "Coding of UE: " << static_cast<int>(UE) << "\n";
    	printf("Coding of s[0]: %i\n", s[0]);
    	printf("Coding of Ü (signed): %i\n", static_cast<char>('Ü'));*/
    
    	cout << AE << " " << OE << " " << UE << " " << ae << " " << oe << " " << ue;
    	return 0;
    }
    


  • In CP850 ist Ä als 142 dezimal kodiert.
    Und afaik ist/war CP850 lange zeit die default codepage für die windows cmd.exe console



  • Sorry @Quiche-Lorraine, aber du hast Codepages nicht verstanden.
    Es geht bei dem Programm von @germanveryhard ja um die Konsolenausgabe, nicht darum, welches Encoding beim Kompilieren verwendet wird (was ja von dem Encoding der ".cpp"-Quelldatei abhängig ist, zumindestens wenn man kein spezielles Encoding, so wie in dem Beispiel von @HarteWare, angibt).

    @germanveryhard: Gib mal mit chcp die aktuelle Codepage auf deiner Konsole aus. Standardmäßig sollte es unter Windows 10 immer noch, wie @firefly schon geschrieben hat, 850 (also "„Multilingual (DOS-Latin-1)“, westeuropäische Sprachen") sein, s.a. Codepages - und dann sollte es so mit deinem Code funktionieren. Oder benutzt du eine IDE und startest daraus dann eine Konsole (dann kann es schon sein, daß diese ein anderes Encoding eingestellt hat)?

    Du kannst aber auch über C++ das Encoding einstellen mit std::locale. Standardmäßig ist "C" (englisch ASCII) eingestellt, aber mittels std::locale::global(std::locale("")); kannst du auf das Encoding deiner Betriebssystem-Umgebung umstellen (dies sollte dann hier in Westeuropa 1252 "Westeuropäische Sprachen" sein), so daß du dann auf die DOS-Umlaute verzichten kannst.

    Einen detaillierten Beitrag dazu gibt es auch unter facet ändern.



  • @Th69 sagte in Probleme mit Umlauten:

    Es geht bei dem Programm von @germanveryhard ja um die Konsolenausgabe, nicht darum, welches Encoding beim Kompilieren verwendet wird

    Jaein, es geht mir auch um die folgende Stelle.

            const unsigned char UE = static_cast<unsigned char>(154);
            const unsigned char ue = static_cast<unsigned char>(129);
            
            string s = "Übung"; 
            if (s[0] == UE){
            	cout << "LALA";
            }
    

    Eigentlich müsste in der Ausgabedatei output.txt ?????????? oder ähnliches stehen und nicht ??????. Tut es aber nicht, da der Vergleich schon fehlschlägt. Warum?



  • @Quiche-Lorraine sagte in Probleme mit Umlauten:

    ... ?????????? oder ähnliches stehen und nicht ??????

    Wolltest du hier auch Unicode-Zeichen posten? 🙂

    Natürlich schlägt der Vergleich schon fehl, da ja das 'Ü' in dem String in dem Encoding der CPP-Quelldatei steht (z.B. als UTF-8 abgespeichert) und eben nicht im Encoding der Konsole.



  • @Th69 sagte in Probleme mit Umlauten:

    Wolltest du hier auch Unicode-Zeichen posten?

    Ja gerne:

    Ž ™ š „ ” 

    🙂

    Aber zurück zum Thema: Ich verstehe die Sache mit dem Encoding der Konsole und dem Vergleich nicht. Was hat denn ein Vergleich der Form if (s[0] == UE) mit dem Encoding der Konsole zu tun?



  • Ja, eben nichts (darum macht es ja auch keinen Sinn die Zeichen auf diese Art zu vergleichen).

    Also entweder:

    // benutze Encoding der Quellcodedatei (d.h. vom Compiler eingestellt)
    string s = "Übung";
    if (s[0] == 'Ü')
    

    oder

    // benutze Encoding der Konsole (falls 850 dort eingestellt ist)
    const unsigned char UE = static_cast<unsigned char>(154);
    string s = std::string(1, (char)UE) + "bung";
    if (s[0] == UE)
    


  • @Quiche-Lorraine sagte in Probleme mit Umlauten:

    Aber zurück zum Thema: Ich verstehe die Sache mit dem Encoding der Konsole und dem Vergleich nicht. Was hat denn ein Vergleich der Form if (s[0] == UE) mit dem Encoding der Konsole zu tun?

    Der TE hat in seinem Code die Variable UE mit dem 'Ü' seiner Windows-Konsole belegt.
    Der String s beginnt aber mit dem 'Ü', wie es in der IDE codiert ist. Deshalb ist s[0] != UE
    Dieser Vergleich würde Sinn machen, wenn er "Übung" via cin von der Konsole eingelesen hätte, dann ergäbe der Vergleich true.

    Starte folgendes einfach mal aus der Konsole und gib ein großes 'Ü' ein:

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
       unsigned char IDE_UE = 'Ü';
       unsigned char CON_UE;
       
       cin >> CON_UE;
       
       cout << "IDE_UE: " << static_cast<int>(IDE_UE) << '\n';
       cout << "CON_UE: " << static_cast<int>(CON_UE) << '\n';
    }
    


  • @Th69 sagte in Probleme mit Umlauten:

    Also entweder:
    // benutze Encoding der Quellcodedatei (d.h. vom Compiler eingestellt)
    string s = "Übung";
    if (s[0] == 'Ü')

    Also etwa so? 🙂

    #include <iostream>
    
    using namespace std;
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("input.txt", "r", stdin);
    	freopen("output.txt", "w", stdout);
    #endif
    	ios_base::sync_with_stdio(false);
    
    	const char AE = 'Ä';
    	const char ae = 'ä';
    	const char OE = 'Ö';
    	const char oe = 'ö';
    	const char UE = 'Ü';
    	const char ue = 'ü';
    	string s = "Übung";
    
    	if (s[0] == UE){
    		cout << "LALA";
    	}
    	/*cout << "Tüpfelhyäneöhrchenstraße\n";
    	printf("Coding of Ü: %i\n", static_cast<unsigned char>('Ü'));	
    	cout << "Coding of UE: " << static_cast<int>(UE) << "\n";
    	printf("Coding of s[0]: %i\n", s[0]);
    	printf("Coding of Ü (signed): %i\n", static_cast<char>('Ü'));*/
    
    	cout << AE << " " << OE << " " << UE << " " << ae << " " << oe << " " << ue;
    	return 0;
    }
    

    Achtung. Ich bin davon ausgegangen das ONLINE_JUDGE nicht definiert wurde, also das stdout Ausgaben nach output.txt umgeleitet werden.

    Der Trick hinter der Sache ist einfach folgender: Die CPP Datei wird in der gleichen Codepage gespeichert, mit welchem die Ausgabedatei output.txt standardmäßig interpretiert wird, mit der Systemcodepage.

    @Belli
    Verstehe ich.

    Ich glaube aber das Beispiel hat ein wenig verwirrt, da je nach definierten Präprozessor-Flag die Ausgabe auf der Console oder nach output.txt landet, im Code unterschiedliche Codierungen verwendet wurden und dadurch die if-Anweisung nicht funktionierte. Vermutlich hat mein erster Beitrag auch ein wenig dazu beigetragen, da ich erst einmal ein Testprogramm zeigte und danach meine Lösung.

    PS:
    Die if Anweisung funktioniert auch wenn ich die CPP Datei in Codepage 850 abspeichere.



  • @Quiche-Lorraine
    Du hast natürlich recht - wenn man den Output in eine Datei umleitet, die dann mit einem Editor öffnet, dann wird sehr wahrscheinlich wieder nicht der Konsolenzeichensatz benutzt ...
    Man muss also schon vorher wissen, ob man in der Konsole 'vernünftigen' Output haben will, oder in einer zu beschreibenden Datei.



  • @HarteWare sagte in Probleme mit Umlauten:

    Das Thema Encoding, Unicode, UTF usw. ist schon etwas verwirrend manchmal... ich verstehe das bis heute nicht 100%, weil es mir einfach zu blöd ist 😃

    Das Problem fängt mit der Kodierung der Quelltextdatei an. Neuere Editoren nutzen da konsequent UTF-8, d.h. eine der möglichen Unicode-Kodierungen, ältere Editoren nutzen da irgend welche 8Bit oder sogar nur 7Bit Kodierungen. Dazu kommt das Thema Zeilenende je nach Plattform durch LF, CR, CR LF, LF CR, NL, … markiert wird. Wenn man also einen Quelltext schreibt, wird dieser von Editor kodiert und abgespeichert. Der Compiler muss in der Lage sein, den Code zu verarbeiten. Wahrscheinlich wird also ein EBCDIC Sourcecode auf einer ASCII Plattform und umgekehrt gar nicht compilieren, weil das z.B. kleine 'a'=0x81 in EBCDIC (das ist auch heute noch wegen der IBM Hosts wichtig) ist und in ASCII 'a'=0x61 ist. D.h. noch nicht einmal der Programmcode ist hier gleich kodiert, über irgend welche Sonderzeichen braucht man sich da erst recht keine Gedanken zu machen. D.h. man muss hier den Quelltext zuerst umkodieren, so dass er auf der jeweiligen Plattform auch verarbeitet werden kann.

    Wenn man nun eine der ASCII Spielarten hat, dann stimmt zumindest meistens (wenn da nicht NRCS genutzt wurde) der Quellcode in der Kodierung überein, d.h. das Programm lässt sich vom Compiler übersetzen und in ein lauffähiges Programm umwandeln. Aber je nachdem welche Kodierung Editor und Compiler nutzen kann es hier schon zu Problemen kommen. Mit NRCS werden unter anderem '[', ']' aus dem ASCII 7-Bit durch 'Ä und 'Ö' ersetzt, u.a. deshalb gab es die Trigraphs in C und C++.

    Am Ende kommt noch die jeweilige Locale Einstellung für das laufende Programm dazu, d.h. je nachdem was da eingestellt ist interpretiert die Konsole die Ein- und Ausgaben des Programms anders.

    Wenn man unbedingt verschiedene Kodierungen in einem Programm nutzen muss, empfiehlt es sich alle Zeichen explizit als Hexcode einzugeben und niemals dabei vergessen, dass das nur mit dem jeweiligen Encoding ein Sinn ergibt. Nimmt man Zeichen direkt z.B. 'a' kann das bereits zu Problemen führen, weil man Rekodieren der Sourcedatei das verändert würde. Will man aber auf einem Windows System ein EBCDIC 'a' verarbeiten können, muss dort der EBCDIC Code stehen und nicht der Windows Code für 'a'. Ansonsten empfiehlt es sich Unicode zu nutzen, da sind dann endlich die Probleme eliminiert, es verbleibt nur noch darauf zu achten wie die Unicode-Zeichen kodiert wurden UTF-8, UTF-16/UCS-2, UCS-4/UTF-32 bzw. UTF-EBCDIC.



  • Würde es hier nicht mehr Sinn machen, erst die Default-Locale zu laden und dann über diese Codepage zu fahren?



  • @eigenartig sagte in Probleme mit Umlauten:

    Würde es hier nicht mehr Sinn machen, erst die Default-Locale zu laden und dann über diese Codepage zu fahren?

    Nur dann wenn der Sourcecode des Programm die gleiche Codepage nutzt.



  • @john-0 sagte in Probleme mit Umlauten:

    Nur dann wenn der Sourcecode des Programm die gleiche Codepage nutzt.

    Das ist dann aber schonmal ein guter Ansatz das so zu machen.



  • Edit: Ach, vergesst meinen Beitrag, ich merke gerade dass der nur einen Teil der Problematik (Ausgabe) behandelte. UTF-16 mit wstring ist hier vielleicht für einen Anfänger am einfachsten zum Üben zu handhaben, wegen dem Index-Zugriff für einen grossen Teil des Coderaums - für vollständig korrekten Produktiv-Code sollte man sich jedoch von Index-Zugriffen auch bei UTF-16 verabschieden 😉

    UTF-8-Ausgabe unter Windows kann man übrigens zuverlässig hinbekommen, indem man die UTF-8-Konsolen-Codepage explizit mit einer WinAPI-Funktion setzt, auch ohne setlocale, wstring, u8-Präfix und dergleichen. Bei UTF-8-Kodierung kann man aber nicht mehr mit s[i] nach Umlauten suchen, sondern muss jedes Sonderzeichen wie einen Substring behandeln (von Kanonischer Normalisierung um verschiedene UTF-8-Codierungen des exakt selben Zeichens finden zu können will ich erst gar nicht anfangen):

    #include <iostream>
    #ifdef _WIN32
         #include <Windows.h>
    #endif
    
    auto main() -> int
    {
        #ifdef _WIN32
            SetConsoleOutputCP(CP_UTF8);
        #endif
        std::cout << "äöüß" << std::endl;
    }
    

    Läuft bei mir unverändert und wie erwartet mit GCC/Clang/MSVC kompiliert unter Windows Eingabeaufforderung, Powershell, MinTTY, WSL-Konsole mit Debian und unter Linux-VM mit Debian.

    Wichtig: Datei als UTF-8 speichern. Unter Visual Studio nennt sich das "Unicode (UTF-8 without signature) - Codepage 65001".



  • @Finnegan Wird das dann auch korrekt auf Systemen angezeigt wo die ausgegebenen Zeichen nicht in der 8-Bit System Codepage darstellbar sind? Also angenommen ich hab Windows auf Codepage 1253 (griechisch, enthält keine deutschen Umlaute) eingestellt, kann ich damit dann ein "ü" ausgeben so dass es korrekt in der Konsole angezeigt wird?



  • @hustbaer sagte in Probleme mit Umlauten:

    @Finnegan Wird das dann auch korrekt auf Systemen angezeigt wo die ausgegebenen Zeichen nicht in der 8-Bit System Codepage darstellbar sind? Also angenommen ich hab Windows auf Codepage 1253 (griechisch, enthält keine deutschen Umlaute) eingestellt, kann ich damit dann ein "ü" ausgeben so dass es korrekt in der Konsole angezeigt wird?

    AFAIK sollte es gehen, da durch SetConsoleOutputCP die console auf UTF-8 umgestellt wird. Die console muss nur ein Font verwenden welche auch die deutschen umlautet enthält.



  • @hustbaer sagte in Probleme mit Umlauten:

    @Finnegan Wird das dann auch korrekt auf Systemen angezeigt wo die ausgegebenen Zeichen nicht in der 8-Bit System Codepage darstellbar sind?

    Umgekehrt zu deinem Griechisch-Beispiel funktionierts: Griechisch und Kyrillisch wird damit ebenfalls problemlos angezeigt. Das sieht mir nicht wie eine "8-Bit-Codepage" aus. Devanagari allerdings nicht mehr. Ich vermute, das ist lediglich durch die Konsolen-Schriftart limitiert. Unter MinTTY hab ich eine sehr umfangreiche Schriftart, dort machen auch japanische Zeichen und Dinge wie "☠✂✐" keine Probleme. Leider kann ich die zum testen nicht in der Standard Windows-Konsole auswählen (die ist sehr pingelig bei den Font-Eigenschaften, da erscheint nicht jede Schrift in der Auswahlliste).

    Die Konsole wird auch mit SetConsoleOutputCP explizit auf UTF-8 umgestellt. Dein "Windows auf Codepage 1253" it läuft dann zumindest in dem einen Konsolenprozess auf UTF-8.



  • @Finnegan sagte in Probleme mit Umlauten:

    Umgekehrt zu deinem Griechisch-Beispiel funktionierts: Griechisch und Kyrillisch wird damit ebenfalls problemlos angezeigt.

    Danke 🙂

    Die Konsole wird auch mit SetConsoleOutputCP explizit auf UTF-8 umgestellt. Dein "Windows auf Codepage 1253" it läuft dann zumindest in dem einen Konsolenprozess auf UTF-8.

    Ich behaupte mal dass das nicht so ist. In der Doku zu SetConsoleOutputCP steht nichts zum Thema System Code Page/CP_ACP. Hätte ich auch noch nie gehört dass sich da was ändert. Zur Info: Die System Code Page ist jene Code Page die bei der UTF-16 <-> ANSI Konvertierung verwendet wird wenn man die "A" Funktionen verwendet (CreateFileA, SetWindowTextA etc.).



  • @hustbaer sagte in Probleme mit Umlauten:

    Ich behaupte mal dass das nicht so ist. In der Doku zu SetConsoleOutputCP steht nichts zum Thema System Code Page/CP_ACP. Hätte ich auch noch nie gehört dass sich da was ändert. Zur Info: Die System Code Page ist jene Code Page die bei der UTF-16 <-> ANSI Konvertierung verwendet wird wenn man die "A" Funktionen verwendet (CreateFileA, SetWindowTextA etc.).

    Ja, das habe ich schlamping formuliert. Der Name legt ja schon nahe, dass es sich nur auf die Konsolen-Ausgabe bezieht. Dass auch File- und andere Konvertierungen betroffen wären, hätte ich auch nicht erwartet.

    Ist dennoch ein netter Ansatz für plattfomübergreifende Software, bei der man sich für UTF-8 entschieden hat. Braucht natürlich UTF-8->UTF--16-Konvertierung für Dinge wie SetWindowText. Mit cout hat man dafür dann wenig Ärger.


Anmelden zum Antworten