Come viene codificato const std :: wstring e come passare a UTF-16
Ho creato questo frammento di esempio C ++ minimo funzionante per confrontare i byte (dalla loro rappresentazione esadecimale) in a std::stringe a std::wstringquando si definisce una stringa con caratteri tedeschi non ASCII in entrambi i tipi.
#include <iostream>
#include <iomanip>
#include <string>
int main(int, char**) {
std::wstring wstr = L"äöüß";
std::string str = "äöüß";
for ( unsigned char c : str ) {
std::cout << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
for ( wchar_t c : wstr ) {
std::cout << std::setw(4) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
return 0;
}
L'output di questo snippet è
c3 a4 c3 b6 c3 bc c3 9f
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178
L'ho eseguito su un PC con Windows 10 64-bit Pro , compilando con MSVC 2019 Community Edition nella versione 16.8.1, utilizzando il sistema di compilazione cmake con quanto segueCMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(wstring VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
add_executable(wstring main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
Ho letto che std::stringsi basano sul chartipo che è un singolo byte. Vedo che l'output del mio snippet indica che str(la std::stringvariabile) è codificata in UTF-8 . Ho letto che i compilatori Microsoft usano wchar_ts con 2 byte per creare std::wstrings (invece di 4 byte wchar_t, ad esempio GNU gcc) e quindi si aspetterebbero che wstr(la std::wstringvariabile) sia (qualsiasi tipo di) codificato UTF-16 . Ma non riesco a capire perché la "ß" (latina diesis s) sia codificata come invece 0x00c30178mi aspettavo 0x00df. Qualcuno possa dirmi:
- Perché sta succedendo?
- Come posso finire con i
std::wstrings codificati UTF-16 (Big Endian andrebbe bene, non mi dispiace un BOM)? Probabilmente ho bisogno di dirlo al compilatore in qualche modo? - Che tipo di codifica è questa?
MODIFICA 1
cambiato il titolo, in quanto non si adattava correttamente alle domande (e in realtà UTF-8 e UTF-16 sono codifiche diverse, quindi io stesso nuovo la risposta già ...)
MODIFICA 2
dimenticavo di menzionare: io uso l' amd64obiettivo del compilatore citato
MODIFICA 3
se aggiungo il /utf-8flag come indicato nei commenti di dxiv (vedi il suo SO-Post collegato ), ottengo l'output desiderato
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df
che assomiglia a UTF-16-BE (senza BOM) per me. Dato che ho avuto problemi con l'ordine corretto dei comandi cmake, questo è il mio CmakeLists.txtfile corrente . È importante mettere il add_compile_optionscomando prima del add_executablecomando (ho aggiunto l'avviso per comodità)
cmake_minimum_required(VERSION 3.0.0)
project(enctest VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
if (MSVC)
message(NOTICE "compiling with MSVC")
add_compile_options(/utf-8)
endif()
add_executable(enctest main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
Trovo il if-endifmodo più leggibile rispetto a quello della sintassi del generatore, ma anche la scrittura funzionerebbe.add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
Nota: per Qt-Projects c'è una bella opzione per il .profile (vedi questo post Qt-Form )
win32 {
QMAKE_CXXFLAGS += /utf-8
}
Ancora la prima parte della mia domanda è aperta: quale codifica è 0x00c30178per "ß" (latino diesis)?
Risposte
Come chiarito nei commenti, il .cppfile sorgente è codificato in UTF-8. Senza un BOM e senza un'opzione esplicita /source-charset:utf-8, il compilatore Visual C ++ assume per impostazione predefinita che il file di origine viene salvato nella codifica tabella codici attiva. Dalla documentazione Set Source Character Set :
Per impostazione predefinita, Visual Studio rileva un indicatore di ordine dei byte per determinare se il file di origine è in un formato Unicode codificato, ad esempio UTF-16 o UTF-8. Se non viene trovato alcun contrassegno per l'ordine dei byte, si presume che il file di origine sia codificato utilizzando la tabella codici utente corrente, a meno che non si specifichi un nome di set di caratteri o una tabella codici utilizzando l'opzione / source-charset.
La codifica UTF-8 di äöüßis C3 A4 C3 B6 C3 BC C3 9F, e quindi la riga:
std::wstring wstr = L"äöüß";
è visto dal compilatore come:
std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;
Supponendo che la tabella codici attiva sia il solito Windows-1252 , i caratteri (estesi) vengono mappati come:
win-1252 char unicode
\xC3 Ã U+00C3
\xA4 ¤ U+00A4
\xB6 ¶ U+00B6
\xBC ¼ U+00BC
\x9F Ÿ U+0178
Pertanto L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"viene tradotto in:
std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;
Per evitare tale (errata) traduzione, è necessario indicare a Visual C ++ che il file di origine è codificato come UTF-8 passando un'opzione del compilatore esplicita /source-charset:utf-8(o /utf-8). Per i progetti basati su CMake, questo può essere fatto utilizzando add_compile_optionscome mostrato in Possibile forzare CMake / MSVC a utilizzare la codifica UTF-8 per i file sorgente senza BOM? C4819 .
quindi ci si aspetterebbe che wstr (la variabile std :: wstring) sia (qualsiasi tipo di) codificato UTF-16
std::wstringnon specifica una codifica. È una sequenza di "caratteri larghi", per alcuni tipi di caratteri larghi (che sono definiti dall'implementazione).
Ci sono sfaccettature di conversione definite nella libreria standard per la conversione in / da codifiche differenti.