`mbstowcs()` Windows vs. Linux



  • 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 zu unsigned 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 &#x60;mbstowcs()&#x60; 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 ein SIGESV.

    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) und mbstowcs (Linux) spuckt das gleiche aus.



  • @Mathuas sagte in &#x60;mbstowcs()&#x60; 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 &#x60;mbstowcs()&#x60; 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 &#x60;mbstowcs()&#x60; 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 wingw32

    x86_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-support

    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:

    #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 &#x60;mbstowcs()&#x60; 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 der msvcrt.dll. Somit müssten doch beide Kompilate das gleiche Ergebnis liefern. Oder übersehe ich das etwas ?



  • @Mathuas sagte in &#x60;mbstowcs()&#x60; Windows vs. Linux:

    @firefly sagte in &#x60;mbstowcs()&#x60; 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 der msvcrt.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 &#x60;mbstowcs()&#x60; 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 ?



  • Kannst es ja mal mit dem DependencyWalker für die kompilierte Datei überprüfen.

    Ansonsten habe ich für LLVM/Clang/LLD (anstatt gcc) unter github.com/mstorsjo/llvm-mingw UCRT-Support gefunden, d.h. Download der neuesten Version unter Releases.



  • @Th69 sagte in &#x60;mbstowcs()&#x60; Windows vs. Linux:

    Kannst es ja mal mit dem DependencyWalker für die kompilierte Datei überprüfen.

    Danke
    Cooles Programm. Ich musste noch die MFC42.dll runterladen.

    Gibt es so etwas auch für Linux ?



  • Such doch einfach mal nach "Linux Dependency Walker"...



  • @Mathuas ldd



  • @Tyrdal
    Danke

    Ich habe gerade noch etwas genialeres entdeckt. Dies kommt fast an das von "Linux Dependency Walker" ran.

    https://github.com/haampie/libtree

    Es ist sogar bei Ubuntu dabei.

    sudo apt install libtree
    

Anmelden zum Antworten