Comment est codé const std :: wstring et comment passer à UTF-16
J'ai créé cet exemple d'extrait d'exemple C ++ minimal pour comparer les octets (par leur représentation hexadécimale) dans a std::string
et a std::wstring
lors de la définition d'une chaîne avec des caractères allemands non ASCII dans l'un ou l'autre type.
#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;
}
La sortie de cet extrait est
c3 a4 c3 b6 c3 bc c3 9f
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178
Je l'ai exécuté sur un PC exécutant lui-même Windows 10 64 bits Pro , en compilant avec MSVC 2019 Community Edition dans la version 16.8.1, en utilisant le système de construction cmake avec les éléments suivantsCMakeLists.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)
J'ai lu, que std::string
s sont basés sur le char
type qui est un octet. Je vois que la sortie de mon extrait de code indique que str
(la std::string
variable) est encodée en UTF-8 . J'ai lu, que les compilateurs Microsoft utilisent des wchar_t
s avec 2 octets pour constituer des std::wstring
s (au lieu de 4 octets wchar_t
par exemple GNU gcc) et s'attendraient donc à ce que wstr
(la std::wstring
variable) soit (n'importe quel type de) encodé en UTF-16 . Mais je ne peux pas comprendre pourquoi le «ß» (latin sharp s) est codé comme 0x00c30178
je m'y attendais à la 0x00df
place. Que quelqu'un me dise:
- Pourquoi cela se produit-il?
- Comment puis-je me retrouver avec des
std::wstring
s encodés en UTF-16 (Big Endian serait bien, je ne me soucie pas d'une nomenclature)? Dois-je probablement dire au compilateur d'une manière ou d'une autre? - De quel type d'encodage s'agit-il?
MODIFIER 1
a changé de titre, car il ne correspondait pas correctement aux questions (et en fait, UTF-8 et UTF-16 sont des encodages différents, donc je suis déjà la nouvelle réponse ...)
MODIFIER 2
oublié de mentionner: j'utilise la amd64
cible du compilateur mentionné
MODIFIER 3
si vous ajoutez le /utf-8
drapeau comme indiqué dans les commentaires de dxiv (voir son SO-Post lié ), j'obtiens le résultat souhaité
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df
qui ressemble à UTF-16-BE (pas de nomenclature) pour moi. Comme j'avais des problèmes avec l'ordre correct des commandes cmake, c'est mon CmakeLists.txt
fichier actuel . Il est important de mettre la add_compile_options
commande avant la add_executable
commande (j'ai ajouté la notice pour plus de commodité)
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)
Je trouve le if-endif
moyen plus lisible que celui de la syntaxe du générateur, mais l'écriture à la place fonctionnerait également.add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
Remarque: pour Qt-Projects, il y a un bon commutateur pour le .pro
fichier (voir ce post Qt-Form )
win32 {
QMAKE_CXXFLAGS += /utf-8
}
La première partie de ma question reste ouverte: quel est le codage 0x00c30178
pour "ß" (latin sharp s)?
Réponses
Comme précisé dans les commentaires, le .cpp
fichier source est encodé en UTF-8. Sans une nomenclature et sans un /source-charset:utf-8
commutateur explicite , le compilateur Visual C ++ par défaut suppose que le fichier source est enregistré dans le codage de page de codes actif. Dans la documentation du jeu de caractères source :
Par défaut, Visual Studio détecte une marque d'ordre des octets pour déterminer si le fichier source est au format Unicode codé, par exemple, UTF-16 ou UTF-8. Si aucune marque d'ordre d'octet n'est trouvée, cela suppose que le fichier source est codé à l'aide de la page de codes utilisateur actuelle, sauf si vous spécifiez un nom de jeu de caractères ou une page de codes à l'aide de l'option / source-charset.
Le codage UTF-8 de äöüß
est C3 A4 C3 B6 C3 BC C3 9F
, et donc la ligne:
std::wstring wstr = L"äöüß";
est vu par le compilateur comme:
std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;
En supposant que la page de codes active soit le Windows-1252 habituel , les caractères (étendus) sont mappés comme suit:
win-1252 char unicode
\xC3 Ã U+00C3
\xA4 ¤ U+00A4
\xB6 ¶ U+00B6
\xBC ¼ U+00BC
\x9F Ÿ U+0178
Par conséquent L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"
est traduit en:
std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;
Pour éviter une telle (mauvaise) traduction, Visual C ++ doit être informé que le fichier source est codé en UTF-8 en passant un commutateur de compilateur explicite /source-charset:utf-8
(ou /utf-8). Pour les projets basés sur CMake, cela peut être fait en utilisant add_compile_options
comme indiqué dans Possible de forcer CMake / MSVC à utiliser le codage UTF-8 pour les fichiers source sans nomenclature? C4819 .
on s'attend donc à ce que wstr (la variable std :: wstring) soit (n'importe quel type de) encodé en UTF-16
std::wstring
ne spécifie pas d'encodage. C'est une séquence de "caractères larges", pour une sorte de caractères larges (qui sont définis par l'implémentation).
Il existe des facettes de conversion définies dans la bibliothèque standard pour la conversion vers / à partir de différents encodages.