`mbstowcs()` Windows vs. Linux



  • @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 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
    


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

    sudo apt install libtree
    

    Danke auch für den Tip. Die Darstellung ist echt deutlich angenehmer zu lesen. Bei Debian ist es übrigens auch dabei.



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

    Danke auch für den Tip. Die Darstellung ist echt deutlich angenehmer zu lesen. Bei Debian ist es übrigens auch dabei.

    Mit diesen Parametern ist es recht ausführlich:

    libtree /bin/ls -vvv -p
    

    Einzig was mich stört. Die lila-Schrift ist kaum zu lesen.



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

    @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 richtigen windows laufen lassen möchte.

    Ich habe male ein wenig LANG= rumgespielt. Spannend was da rauskommt.

    #include <stdio.h>
    #include <locale.h>
    #include <time.h>
    
    int main(void)
    {
      time_t currtime;
      struct tm *timer;
      char buffer[80];
    
      time( &currtime );
      timer = localtime( &currtime );
    
      printf("\nsetlocale: %s\n", setlocale(LC_ALL, ""));
      strftime(buffer,80,"%c", timer );
      printf("Float: %f   Date: %s\n\n", 123.456, buffer) ;
    
      return 0;
    }
    

    Copilierscript:

    echo 
    echo ======== Linux ========
    echo 
    rm main
    gcc -o main main.c 
    LANG="de_CH.UTF8"  ./main
    LANG="de_DE.UTF8"  ./main
    LANG="en_US.UTF8"  ./main
    LANG="en_GB.UTF8"  ./main
    echo 
    echo ======== Windows ========
    echo 
    rm main.exe
    x86_64-w64-mingw32-gcc main.c -o main.exe
    LANG="de_CH.UTF8" wine main.exe
    LANG="de_DE.UTF8" wine main.exe
    LANG="en_US.UTF8" wine main.exe
    LANG="en_GB.UTF8" wine main.exe
    

    Ausgabe:

    ======== Linux ========
    
    
    setlocale: de_CH.UTF8
    Float: 123.456000   Date: Mo 03 Jun 2024 17:31:02
    
    
    setlocale: de_DE.UTF8
    Float: 123,456000   Date: Mo 03 Jun 2024 17:31:02 CEST
    
    
    setlocale: en_US.UTF8
    Float: 123.456000   Date: Mon 03 Jun 2024 05:31:02 PM CEST
    
    
    setlocale: en_GB.UTF8
    Float: 123.456000   Date: Mon 03 Jun 2024 17:31:02 CEST
    
    
    ======== Windows ========
    
    
    setlocale: German_Switzerland.1252
    Float: 123.456000   Date: 3.6.2024 17:31:03
    
    
    setlocale: German_Germany.1252
    Float: 123,456000   Date: 3.6.2024 17:31:03
    
    
    setlocale: English_United States.1252
    Float: 123.456000   Date: 6/3/2024 5:31:03 PM
    
    
    setlocale: English_United Kingdom.1252
    Float: 123.456000   Date: 03/06/2024 17:31:03
    

    Es wird alles berücksichtigt, inklusive das unsympathische Komma beim Float für die Deutschen.

    Was noch interessant ist, wieso kommt das Datum bei wine anders raus als unter Linux ?


Anmelden zum Antworten