`mbstowcs()` Windows vs. Linux
-
Windows ist hier mal wieder besonders. Die bezeichnungen der locale ist anders als unter linux...
Schau mal in der doku nach wie die bennenung ist.Du schreibst von source und dest, aber man kann nur einen Parameter für Codepage mitgeben.
Stimmt und zwar des source strings. Ergebnis ist ein UTF-16 string. ANderes wirst du unter Windows auch mit mbstowcs nicht bekommen.
Denn diese Methoden konvertieren von einem byte encoding zu wide char encoding mit wchar_t als datentyp.
und unter windows ist dieser nunmal nur 16Bit groß und wird von der windows api für UTF-16 strings genutzt...Da die verschiedenen UTF Kodierungen sich nur unterscheiden welchen basis datentyp verwendet wird lässt sich recht einfach zwischen den verschiedenen Kodierungen konvertieren.
Falls du C++-11 nutzen kannst, dann liefert die c++ standard library passende methoden für so eine konvertierung an.
-
@firefly sagte in `mbstowcs()` Windows vs. Linux:
@john-0 sagte in `mbstowcs()` Windows vs. Linux:
Windows hingegen setzte auf eine 16Bit Kodierung der Code Points (zuerst UCS-2 und dann UTF-16), so dass zu Problemen kam, wenn man einen UCS-4 String mit vollem 32Bit Umfang nutzt.
Für UCS-2 würde ich dem zustimmen aber nicht für UTF-16.
UCS-2 kann trivialerweise nur 16Bit = 65536 Code Points kodieren, das war zuwenig und reichte nur für die Basic Multilingual Plane aus. UTF-16 ist hingegen so definiert, dass Unicode Code Points entweder in 16Bit oder in zwei aufeinanderfolgenden 2×16Bit kodiert werden. Da man mindestens ein Bit im ersten 16Bit Wert benötigt um anzuzeigen, dass ein weiterer 16Bit Wert folgt, sind so maximal 31Bit verfügbar. Es gibt aber weitere Einschränkungen wegen der UTF-16 Kompatibilität sind die Surrogates für Unicode Code Points auch geblockt.
UCS-4 erlaubte Werte von 0x00000000 bis 0xFFFFFFFF für Unicode Code Points, die ließen sich allesamt in UTF-8 kodieren und dekodieren. Davon wurden damals nur ein Bruchteil auch wirklich als gültige Code Points definiert, aber Software sollte damals so geschrieben werden, dass sie mit den kompletten 32Bit umgehen können sollte.
Wegen Windows wurde dann UTF-16 eingeführt, und UCS-4 abgeschafft und durch UTF-32 ersetzt. Zwar sind die definierten gültigen Code Points bei UCS-4 und UTF-32 identisch, aber der mögliche Wertebereich für Code Points wurde für UTF-32 deutlich eingeschränkt, so dass UTF-16 überhaupt erst möglich wurde. Also anstatt 0x00000000 bis 0xFFFFFFFF ist nur noch 0x00000000 bis 0x0010FFFF abzüglich der Surrogates möglich. Das führt nun dazu, dass UTF-8 Code Points in maximal 4 Bytes kodiert und nicht mehr in maximal 6 Bytes wie für UCS-4.
-
@Mathuas sagte in `mbstowcs()` Windows vs. Linux:
mbstowcs
MS versteht unter einem Breitzeichen grundsätzlich eine 16 Bit-Darstellung. Also UTF16. Wenn Du UTF32 haben willst, musst Du zunächst aus der vorliegenden UTF8-Bytesequenz den Codepoint berechnen. Und mit dem Codepoint ergeben sich dann die Bytes für UTF32, UTF16 und natürlich für jede andere Zeichenkodierung.
Die Algorithmen nach c++ umzusetzen dürfte kein Problem sein. MFG
-
Mir ist gerade in den Sinn gekommen, das ich mal bei X11 ein ähnliches Problem hatte, das hatte man es auf folgende Art gelöst.
Dies habe ich jetzt abgeändert, und dies klappt mit Linux und Windows. XChar2b ist fast gleich wie ein UTF8, nur das die Bytes vertauscht sind.
Es verwundert mich echt, das in keiner C-lib diese Funktion fertig zur Verfügung gestellt wird.
Haben alle X11 Programmierer welche UTF8 und X11 verwendeten die Funktion selbst geschriebe ?
Oder auch welche, die FreeTypes verwenden ?
-
@Mathuas sagte in `mbstowcs()` Windows vs. Linux:
Keiner ein Idee ?
Mein UTF8-String sieht so aus: "ŸAÄÖÜ" und Länge ermitteln und konvenieren tue ich mit
mbstowcs()
Unter Linux wird folgendes ausgegeben, was in meinen Augen auch richtig ist, weil dort funktioniert
FT_Load_Char()
ŸAÄÖÜ len_UTF8: 9 len_UTF32: 5 0x00000178 - 0x00000041 - 0x000000C4 - 0x000000D6 - 0x000000DC
Unter Windows mit wine kommt folgendes raus:
┼©A├ä├û├£ len_UTF8: 9 len_UTF32: 9 0x00B800C5 - 0x00C30041 - 0x00C30084 - 0x00C30096 - 0x0000009C - 0x00000000 - 0x00000000 - 0x00000000 - 0x00000000
Öhm sicher das unter windows dein input string valides utf8 ist?
Denn wenn ich dein Beispiel in https://onlinetools.com/utf8/convert-utf8-to-utf16 eingebe kommt folgendes raus:0x0178 0x0041 0x00c4 0x00d6 0x00dc
Und das ist auch das ergebnis wie unter linux nur halt in 16bit statt 32bit.
Was auch kein wunder ist. Denn die test zeichen können in 16Bit abgebildet werden.Was für einen compiler hast du genutzt für den windows build?
Gut möglich dass der compiler den text nicht als utf8 interpretiert hat sondern als eine ANSI codepage. (wenn der test string bestandteil des sourcodes war. Oder die consolen locale nicht auf utf-8 eingestellt war unter windows)
Gib doch mal die einzelnen bytes deines test strings als integer aus. Dann siehst du ob der test string korrektes utf-8 ist oder nicht.
Hier die korrekten werte des utf-8 test strings in bytes:c5 b8 41 c3 84 c3 96 c3 9c
WEnn der test string bestandteil des sourcecodes ist dann kannst du diesen auch wie folgt schreiben, damit dieser definitiv utf-8 ist
"\u0178\u0041\u00c4\u00d6\u00dc"
Und irgendwie kann ich nicht glauben dass die verwendung von XChar2b dein Problem unter windows wirklich alleine gefixt hat. Du musst noch mehr an deinem code geändert haben, damit es auch unter windows funktioniert. Oder wie du dein test programm gestartet hast.
Denn XChar2b ist ein struct mit 2 bytes aka eine 16Bit datenstruktur...
Und der von dir verlinkte Code macht im grunde nichts anderes als UTF-8 nach UTF-16 zu konvertieren. Unter Windows macht die mbstowcs nichts anderes (wenn die process locale auf eine korrekte utf-8 locale gesetzt ist) bzw. die Verwendung von MultiByteToWideChar da kannst du explizit angeben dass der input string UTF-8 ist.Dein Problem ist, dass unter windows vieles anders läuft als unter linux was default text encondings von compiler und runtime betrifft (Für text, welcher in einem byte encoding codiert ist)
Ein teil der C-API, welches ein "char*" als parameter hat, unter windows kann nichts mit utf-8 anfangen. Das betrifft besonders die C-API für das öffnen von dateien, wie fopen!
Und ein großteil der Windows API die ein char* als parameter nimmt betrifft das auch.
Da werden die zeichen als ASCII/ANSI interpretiert.
-
Was für einen compiler hast du genutzt für den windows build?
Gut möglich dass der compiler den text nicht als utf8 interpretiert hat sondern als eine ANSI codepage. (wenn der test string bestandteil des sourcodes war. Oder die consolen locale nicht auf utf-8 eingestellt war unter windows)
Gib doch mal die einzelnen bytes deines test strings als integer aus. Dann siehst du ob der test string korrektes utf-8 ist oder nicht.
Hier die korrekten werte des utf-8 test strings in bytes:Dies habe ich gemacht
#include <stdio.h> #include <string.h> const char * TestString = "ABCÄÖÜŸ"; int main(int argc, char **argv) { for (int i=0; i<strlen(TestString); i++) { printf("%i - ", TestString[i]); } printf("\n"); }
Linux:
$ gcc -o main main.cpp $ ./main 65 - 66 - 67 - -61 - -124 - -61 - -106 - -61 - -100 - -59 - -72 -
Windows:
$ x86_64-w64-mingw32-gcc main.cpp -o main.exe $ wine main.exe 002c:fixme:winediag:loader_init wine-staging 9.9 is a testing version containing experimental patches. 002c:fixme:winediag:loader_init Please mention your exact version when filing bug reports on winehq.org. 65 - 66 - 67 - -61 - -124 - -61 - -106 - -61 - -100 - -59 - -72 -
PS: Ich sehe gerade, das zT. 2 '-' hintereinander kommen.
-
Das ist das Minuszeichen bei den Werten...
Kannst ja mal%u
oder%x
verwenden (mit Cast zuunsigned char
).
-
Und nutze für den vergleich deinen original test string... und nicht einen anderen...
Da du dein Programm unter unter linux mit mingw kompilierst und es dann unter windows (via wine) laufen lässt klar wie die ausgabe zustande kommt.In diesem Beispiel ist nicht die kodierung des Sourcecodes das Problem, sondern dass unter windows by default die console kein UTF-8 kann sondern ausgegebene byte sequencen auf stdout (z.b. via printf) als ASCII/ANSI zeichen interpretiert.
Und zusätzlich der process eine ASCII/ANSI locale verwendet wodurch mbstowcs den input string nicht als utf-8 interpretiert!Du solltest für den windows build besser MultiByteToWideChar verwenden statt mbstowcs. Dadurch wird definitiv der input als utf-8 interpretiert.
also z.b. so (Fehlerhandling fehlt hier!):
// wchars_num enthält auch den null-terminator da -1 für den cbMultiByte parameter übergeben wurde // Zu erst die benötigte länge des ziel strings ermitteln int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , TestString , -1, NULL , 0 ); wchar_t* wstr = new wchar_t[wchars_num]; // DIe eigendliche konvertierung durchführen MultiByteToWideChar( CP_UTF8 , 0 , TestString , -1, wstr , wchars_num ); // Mach was mit dem string delete[] wstr;
-
@firefly sagte in `mbstowcs()` Windows vs. Linux:
Du solltest für den windows build besser MultiByteToWideChar verwenden statt mbstowcs. Dadurch wird definitiv der input als utf-8 interpretiert.
Jetzt habe ich mal ein sauberes Testprogramm gemacht.
setlocale(LC_ALL, "")
scheint recht wichtig zu sein. Ansonsten gibt es zumindest unter Linux einSIGESV.
Das Testprogramm:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <locale.h> #if defined(_WIN64) #include <windows.h> #endif void WideChar_Test(const char * str){ printf("wchar_t size: %li\n",sizeof(wchar_t)); printf("UTF8: (%li) ", strlen(str)); for (int i=0; i<strlen(str); i++) { printf("%hhu - ", str[i]); } printf("\n"); size_t len = mbstowcs(nullptr, str, strlen(str)); wchar_t * wc = (wchar_t *) malloc(len * sizeof(wchar_t)); mbstowcs(wc, str, len); printf("WideStr: (%li) ", len); for (int i=0; i<len; i++) { printf("%hhu - ", wc[i]); } printf("\n\n"); free(wc); } const char * TestString1 = "12345678"; const char * TestString2 = "ABCÄÖÜŸ"; #if defined(_WIN64) void WideChar_Windows(const char * str){ printf("wchar_t size: %li\n",sizeof(wchar_t)); printf("UTF8: (%li) ", strlen(str)); for (int i=0; i<strlen(str); i++) { printf("%hhu - ", str[i]); } printf("\n"); size_t len = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0); wchar_t * wc = (wchar_t *) malloc(len * sizeof(wchar_t)); MultiByteToWideChar( CP_UTF8, 0, str, -1, wc, len); printf("WideStr: (%li) ", len); for (int i=0; i<len; i++) { printf("%hhu - ", wc[i]); } printf("\n\n"); free(wc); } #endif int main(int argc, char **argv) { printf("Testring 1: %s\n", TestString1); printf("Testring 2: %s\n", TestString2); // printf("setlocale: %s", setlocale(LC_ALL, "de_CH.utf8")); printf("setlocale: %s\n", setlocale(LC_ALL, "")); printf("--- Testring 1 ---\n"); WideChar_Test(TestString1); printf("--- Testring 2 ---\n"); WideChar_Test(TestString2); #if defined(_WIN64) printf("--- MultiByteToWideChar Testring 1 ---\n"); WideChar_Windows(TestString1); printf("--- MultiByteToWideChar Testring 2 ---\n"); WideChar_Windows(TestString2); #endif return 0; }
Das compilieren:
echo echo ======== Linux ======== echo rm main gcc -o main main.cpp ./main echo echo ======== Windows ======== echo rm main.exe x86_64-w64-mingw32-gcc main.cpp -o main.exe wine main.exe
Dies Ausgabe:
$ ./compile.sh ======== Linux ======== Testring 1: 12345678 Testring 2: ABCÄÖÜŸ setlocale: de_CH.UTF-8 --- Testring 1 --- wchar_t size: 4 UTF8: (8) 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - WideStr: (8) 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - --- Testring 2 --- wchar_t size: 4 UTF8: (11) 65 - 66 - 67 - 195 - 132 - 195 - 150 - 195 - 156 - 197 - 184 - WideStr: (7) 65 - 66 - 67 - 196 - 214 - 220 - 120 - ======== Windows ======== 002c:fixme:winediag:loader_init wine-staging 9.9 is a testing version containing experimental patches. 002c:fixme:winediag:loader_init Please mention your exact version when filing bug reports on winehq.org. Testring 1: 12345678 Testring 2: ABCà Ãß setlocale: German_Switzerland.1252 --- Testring 1 --- wchar_t size: 2 UTF8: (8) 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - WideStr: (8) 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - --- Testring 2 --- wchar_t size: 2 UTF8: (11) 65 - 66 - 67 - 195 - 132 - 195 - 150 - 195 - 156 - 197 - 184 - WideStr: (11) 65 - 66 - 67 - 195 - 30 - 195 - 19 - 195 - 83 - 197 - 184 - --- MultiByteToWideChar Testring 1 --- wchar_t size: 2 UTF8: (8) 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - WideStr: (9) 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - 0 - --- MultiByteToWideChar Testring 2 --- wchar_t size: 2 UTF8: (11) 65 - 66 - 67 - 195 - 132 - 195 - 150 - 195 - 156 - 197 - 184 - WideStr: (8) 65 - 66 - 67 - 196 - 214 - 220 - 120 - 0 -
Was am Anfang schon auffällt, Die Windows Konsole kommt mit UTF8 nicht zurecht.
MultiByteToWideChar
(Windows) undmbstowcs
(Linux) spuckt das gleiche aus.
-
@Mathuas sagte in `mbstowcs()` Windows vs. Linux:
Was am Anfang schon auffällt, Die Windows Konsole kommt mit UTF8 nicht zurecht.
Ja weil windows bei default in der CLI für non "Unicode" (Unter windows ist UNICODE = UTF-16 aka wchar_t) eine ANSI codepage nutzt. Du musst die windows console explizit in den utf-8 modus schalten (afaik durch das setzen einer passenden codepage in der windows console.
Wobei das AFAIK jetzt nur für die cmd.exe gilt.
MultiByteToWideChar scheint jetzt auch zu funktionieren. mbstowcs Kommt unter Windows nicht zurecht.
Öhm wieso mbstowcs nicht funktioniert hab ich dir schon geschrieben...
Und in deiner ausgabe für den windows build siehst du es auch:setlocale: German_Switzerland.1252
das ist keine utf-8 locale... Sondern eine ANSI codepage locale...
ABer das ganze habe ich dir schon mitgeteilt aber scheinbar ignorierst du diese Informationen komplett...
-
Ich habe die Beispiel von hier probiert, bei Windows spuckt es überall
(null)
.https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-170
Ausser wen ich es ausschreibe wie hier:
printf("setlocale: %s\n", setlocale(LC_ALL, "German_Switzerland.1252"));
Sowas geht nicht
".UTF8"
-
Versuche es mal beim Aufruf anzugeben, s. Changing the Locale in WINE, anstatt mittels
setlocale
im Code:
LANG="de_CH.UTF8" wine main.exe
bzw.
LC_ALL="de_CH.UTF8" wine main.exe
(hier benutzt du die Linux-Nomenklatur, anstatt im Code die Windows-Runtime Nomenklatur)
-
entweder ist das eine eigenart von wine, mingw oder du machst was falsch...
Folgender code unter windows mit visual studio 2019 liefert non null zurück:
#include <stdio.h> #include <locale.h> int main(void) { printf("setlocale: %s\n", setlocale(LC_ALL, ".UTF8")); return 0; }
-
@Th69 sagte in `mbstowcs()` Windows vs. Linux:
Versuche es mal beim Aufruf anzugeben, s. Changing the Locale in WINE, anstatt mittels
setlocale
im Code:
LANG="de_CH.UTF8" wine main.exe
bzw.
LC_ALL="de_CH.UTF8" wine main.exe
(hier benutzt du die Linux-Nomenklatur, anstatt im Code die Windows-Runtime Nomenklatur)Nützt ihm aber nicht viel wenn er sein Programm unter einem richtigne windows laufen lassen möchte..
-
Das ist ja auch ersteinmal als Test gedacht, ob es mit Wine so funktioniert.
Ansonsten müßte er sein Programm unter einem echten Windows 10/11 (z.B. in einer VM) laufen lassen, da ".UTF8" erst dort eingeführt wurde:Ab Windows 10, Version 1803 (10.0.17134.0), unterstützt die universelle C-Runtime die Verwendung einer UTF-8-Codepage.
Und wenn ich die Doku richtig verstehe, so müßte man unter Windows den Bindestrich (statt Unterstrich) verwenden, z.B.
"de-CH.UTF8"
.
-
@firefly sagte in `mbstowcs()` Windows vs. Linux:
entweder ist das eine eigenart von wine, mingw oder du machst was falsch...
Folgender code unter windows mit visual studio 2019 liefert non null zurück:
#include <stdio.h> #include <locale.h> int main(void) { printf("setlocale: %s\n", setlocale(LC_ALL, ".UTF8")); return 0; }
Ich habe dies gerade in der VB mit Win10 probiert. Es kommt auch
(null)
Compiliert habe ich es mit wingw32x86_64-w64-mingw32-gcc main.cpp -o main.exe
Mit einem
(LC_ALL, "")
spuckt es in wine und VB-Win10 das gleiche aus:setlocale: German_Switzerland.1252
-
Es ist ein mingw problem.
https://stackoverflow.com/questions/73185769/build-a-program-with-mingw-on-linux-and-run-under-wine-utf-8-supportWEnn ich folgenden code unter windows mit visual studio (als mit dem microsoft compiler) im release build baue (unter Windows 10 22H2) und dann das binary unter linux mit wine starte funktioniert es:
#include <stdio.h> #include <locale.h> int main(void) { printf("setlocale: %s\n", setlocale(LC_ALL, ".utf8")); printf("%s\n", "\xc5\xb8\x41\xc3\x84\xc3\x96\xc3\x9c"); return 0; }
Output:
setlocale: English_United Kingdom.65001 ŸAÄÖÜ
Beachte die .65001 statt .1252 am ende der locale ausgabe.
65001 ist die codepage nummer für UTF-8
-
@firefly sagte in `mbstowcs()` Windows vs. Linux:
WEnn ich folgenden code unter windows mit visual studio (als mit dem microsoft compiler) im release build baue (unter Windows 10 22H2) und dann das binary unter linux mit wine starte funktioniert es:
Recht merkwürdig
setlocale
ist doch in dermsvcrt.dll
. Somit müssten doch beide Kompilate das gleiche Ergebnis liefern. Oder übersehe ich das etwas ?
-
@Mathuas sagte in `mbstowcs()` Windows vs. Linux:
@firefly sagte in `mbstowcs()` Windows vs. Linux:
WEnn ich folgenden code unter windows mit visual studio (als mit dem microsoft compiler) im release build baue (unter Windows 10 22H2) und dann das binary unter linux mit wine starte funktioniert es:
Recht merkwürdig
setlocale
ist doch in dermsvcrt.dll
. Somit müssten doch beide Kompilate das gleiche Ergebnis liefern. Oder übersehe ich das etwas ?Ja denn die "alte" C-Runtime (via msvcrt.dll) von Windows unterstützt kein UTF-8.
Sondern erst mit der "neuen" C-Runtime (UCRT) wird UTF-8 in der C-Runtime unterstützt.Wie man hier sehen kann
https://gist.github.com/minoki/c3d65d15982f17db0801e8a9b06333bf
(siehe die ausgaben wo versucht wird eine UTF-8 locale zu setzen.Oder auch hier beschrieben:
https://blog.r-project.org/2022/11/07/issues-while-switching-r-to-utf-8-and-ucrt-on-windows/https://stackoverflow.com/questions/67848972/differences-between-msvcrt-ucrt-and-vcruntime-libraries
Und deine mingw version nutzt die "alte" MS C-Runtime
-
@firefly sagte in `mbstowcs()` Windows vs. Linux:
Ja denn die "alte" C-Runtime (via msvcrt.dll) von Windows unterstützt kein UTF-8.
Sondern erst mit der "neuen" C-Runtime (UCRT) wird UTF-8 in der C-Runtime unterstützt.Wen ich es richtig verstehe, müsste man
ucrtbase.dll
einbinden ?