2017-08-01 2 views
2

Ich versuche wchar_t* mit UTF-8-Zeichen mit vsnprintf zu formatieren und dann den Puffer mit printf zu drucken.wchar_t * mit UTF8-Zeichen in MSVC

den folgenden Code Gegeben:

/* 
    This code is modified version of KB sample: 
    https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rtref/vsnprintf.htm 

    The usage of `setlocale` is required by my real-world scenario, 
    but can be modified if that fixes the issue. 
*/ 

#include <wchar.h> 
#include <stdarg.h> 
#include <stdio.h> 
#include <locale.h> 

#ifdef MSVC 
#include <windows.h> 
#endif 

void vout(char *string, char *fmt, ...) 
{ 
    setlocale(LC_CTYPE, "en_US.UTF-8"); 
    va_list arg_ptr; 

    va_start(arg_ptr, fmt); 
    vsnprintf(string, 100, fmt, arg_ptr); 
    va_end(arg_ptr); 
} 

int main(void) 
{ 
    setlocale(LC_ALL, ""); 
#ifdef MSVC 
    SetConsoleOutputCP(65001); // with or without; no dice 
#endif 

    char string[100]; 

    wchar_t arr[] = { 0x0119 }; 
    vout(string, "%ls", arr); 
    printf("This string should have 'ę' (e with ogonek/tail) after colon: %s\n", string); 
    return 0; 
} 

ich mit gcc v5.4 auf Ubuntu 16 kompiliert, um die gewünschte Ausgabe in BASH zu erhalten:

gcc test.c -o test_vsn 
./test_vsn 
This string should have 'ę' (e with ogonek/tail) after colon: ę 

jedoch auf Windows 10 mit CL v19. 10,25019 (VS 2017), bekomme ich seltsame Ausgabe in CMD:

cl test.c /Fetest_vsn /utf-8 
.\test_vsn 
This string should have 'T' (e with ogonek/tail) after colon: e 

(die ę vor Kolon beco mes T und nach dem Doppelpunkt ist e ohne ogonek)

Bitte beachte, dass ich CL neue /utf-8 Schalter verwendet (eingeführt in VS 2015), die offenbar keine Wirkung mit oder ohne hat. Auf der Grundlage ihrer blog post:

Es gibt auch eine/utf-8-Option, die ein Synonym für die Einstellung ist „/ source-charset: utf-8“ und „/ Ausführungs-charset: utf-8“.

(meine Quelldatei hat bereits BOM/utf8'ness und Ausführung-charset hilft anscheinend nicht)

Was die minimale Menge an Änderungen an den Code/Compiler-Schalter könnte die Ausgabe identisch aussehen zu machen dem von gcc?

+2

Unter Windows unterstützt 'printf()' (und die Konsole im Allgemeinen) UTF-8 nicht. Sie könnten 'WideCharToMultiByte()' (oder gleichwertig) verwenden, um UTF-16-codierte 'wchar_t'-Daten in UTF-8 zu konvertieren, aber das ist immer noch keine Garantie, dass die Konsole es korrekt anzeigt. Sie sollten Unicode-Daten mit Unicode-Konsolen-APIs wie der Win32-Funktion 'WriteConsoleW()' oder 'std :: wcout' in C++ in die Konsole schreiben. Es gibt viele Fragen zu StackOverflow zur Ausgabe von Unicode-Daten an eine Windows-Konsole. Ihr Ruf ist hoch genug, dass Sie wissen sollten, bevor Sie fragen. –

+0

Sie können auch die PowerShell IDE ausführen und zum Verzeichnis Ihres Programms navigieren und dann Ihr Programm ausführen. –

+0

@RemyLebeau, danke. Ich werde 'WideCharToMultiByte()' und andere Unicode-Konsolen-APIs ausprobieren. Ich habe etwas recherchiert, bin aber in der Produktversionierung verloren gegangen (z. B. seit VS2015, der Einbeziehung von vsnprintf OOTB usw.). Werde mehr lesen. :) –

Antwort

0

Basierend auf @ RemyLebeau Kommentar, habe ich den Code geändert, um w Variante der printf-APIs zu verwenden, um die Ausgabe identisch mit msvc unter Windows, die gleiche wie gcc unter Unix.

Zusätzlich habe ich jetzt anstelle von Codepage _setmode (FILE Übersetzungsmodus) verwendet.

/* 
    This code is modified version of KB sample: 
    https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rtref/vsnprintf.htm 

    The usage of `setlocale` is required by my real-world scenario, 
    but can be modified if that fixes the issue. 
*/ 

#include <wchar.h> 
#include <stdarg.h> 
#include <stdio.h> 
#include <locale.h> 

#ifdef _WIN32 
#include <io.h> //for _setmode 
#include <fcntl.h> //for _O_U16TEXT 
#endif 

void vout(wchar_t *string, wchar_t *fmt, ...) 
{ 
    setlocale(LC_CTYPE, "en_US.UTF-8"); 
    va_list arg_ptr; 

    va_start(arg_ptr, fmt); 
    vswprintf(string, 100, fmt, arg_ptr); 
    va_end(arg_ptr); 
} 

int main(void) 
{ 
    setlocale(LC_ALL, ""); 
#ifdef _WIN32 
    int oldmode = _setmode(_fileno(stdout), _O_U16TEXT); 
#endif 

    wchar_t string[100]; 

    wchar_t arr[] = { 0x0119, L'\0' }; 
    vout(string, L"%ls", arr); 
    wprintf(L"This string should have 'ę' (e with ogonek/tail) after colon: %ls\r\n", string); 

#ifdef _WIN32 
    _setmode(_fileno(stdout), oldmode); 
#endif 
    return 0; 
} 

Alternativ können wir fwprintf verwenden und stdout als erstes Argument liefern. Um das gleiche mit fwprintf(stderr,format,args) (oder perror(format, args)) zu tun, müssten wir auch _setmode die stderr.