Comment est codé const std :: wstring et comment passer à UTF-16

Nov 30 2020

J'ai créé cet exemple d'extrait d'exemple C ++ minimal pour comparer les octets (par leur représentation hexadécimale) dans a std::stringet a std::wstringlors 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::strings sont basés sur le chartype qui est un octet. Je vois que la sortie de mon extrait de code indique que str(la std::stringvariable) est encodée en UTF-8 . J'ai lu, que les compilateurs Microsoft utilisent des wchar_ts avec 2 octets pour constituer des std::wstrings (au lieu de 4 octets wchar_tpar exemple GNU gcc) et s'attendraient donc à ce que wstr(la std::wstringvariable) soit (n'importe quel type de) encodé en UTF-16 . Mais je ne peux pas comprendre pourquoi le «ß» (latin sharp s) est codé comme 0x00c30178je m'y attendais à la 0x00dfplace. Que quelqu'un me dise:

  • Pourquoi cela se produit-il?
  • Comment puis-je me retrouver avec des std::wstrings 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 amd64cible du compilateur mentionné

MODIFIER 3

si vous ajoutez le /utf-8drapeau 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.txtfichier actuel . Il est important de mettre la add_compile_optionscommande avant la add_executablecommande (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-endifmoyen 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 .profichier (voir ce post Qt-Form )

win32 {
    QMAKE_CXXFLAGS += /utf-8
}

La première partie de ma question reste ouverte: quel est le codage 0x00c30178pour "ß" (latin sharp s)?

Réponses

5 dxiv Dec 01 2020 at 16:41

Comme précisé dans les commentaires, le .cppfichier source est encodé en UTF-8. Sans une nomenclature et sans un /source-charset:utf-8commutateur 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_optionscomme indiqué dans Possible de forcer CMake / MSVC à utiliser le codage UTF-8 pour les fichiers source sans nomenclature? C4819 .

MarshallClow Nov 30 2020 at 20:50

on s'attend donc à ce que wstr (la variable std :: wstring) soit (n'importe quel type de) encodé en UTF-16

std::wstringne 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.