Como const std :: wstring é codificado e como mudar para UTF-16
Criei este mínimo de trabalho C ++ exemplo trecho para comparar bytes (por sua representação hex) em um std::string
e uma std::wstring
, quando definindo uma cadeia com caracteres não ASCII alemão em qualquer um dos tipos.
#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;
}
A saída deste snippet é
c3 a4 c3 b6 c3 bc c3 9f
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178
Eu executei isso em um PC executando o próprio Windows 10 Pro de 64 bits , compilando com MSVC 2019 Community Edition na versão 16.8.1, usando o sistema de compilação cmake com o seguinteCMakeLists.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)
Eu li, que std::string
s são baseados no char
tipo que é um único byte. Vejo que a saída do meu snippet indica que str
(a std::string
variável) é codificada em UTF-8 . Eu li que os compiladores da Microsoft usam wchar_t
s com 2 bytes para formar std::wstring
s (em vez de 4 bytes, wchar_t
por exemplo, GNU gcc) e, portanto, esperariam que wstr
(a std::wstring
variável) fosse (qualquer tipo de) codificado em UTF-16 . Mas não consigo entender por que o "ß" (s em sustenido em latim) está codificado como 0x00c30178
eu esperava 0x00df
. Alguém pode me dizer:
- Porque isso está acontecendo?
- Como posso terminar com
std::wstring
s codificados em UTF-16 (Big Endian seria bom, não me importo com um BOM)? Eu provavelmente preciso dizer ao compilador de alguma forma? - Que tipo de codificação é essa?
EDITAR 1
mudou o título, pois não se encaixava nas perguntas corretamente (e, na verdade, UTF-8 e UTF-16 são codificações diferentes, então eu já sei a resposta ...)
EDITAR 2
esqueci de mencionar: eu uso o amd64
destino do compilador mencionado
EDITAR 3
se adicionar o /utf-8
sinalizador como apontado nos comentários de dxiv (veja seu SO-Post vinculado ), obtenho a saída desejada
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df
que se parece com UTF-16-BE (sem BOM) para mim. Como tive problemas com a ordem correta dos comandos cmake, este é o meu CmakeLists.txt
arquivo atual . É importante colocar o add_compile_options
comando antes do add_executable
comando (adicionei o Aviso por conveniência)
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)
Acho o if-endif
caminho mais legível do que o gerador de sintaxe, mas escrever também funcionaria.add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
Nota: Para Qt-Projects, há uma boa opção para o .pro
arquivo (veja esta postagem do Qt-Form )
win32 {
QMAKE_CXXFLAGS += /utf-8
}
Ainda assim, a primeira parte da minha pergunta está aberta: qual codificação é 0x00c30178
para "ß" (latin sharp s)?
Respostas
Conforme esclarecido nos comentários, o .cpp
arquivo de origem é codificado em UTF-8. Sem um BOM e sem uma /source-charset:utf-8
opção explícita , o compilador Visual C ++ assume como padrão o arquivo de origem é salvo na codificação de página de código ativa. Da documentação do conjunto de caracteres de origem :
Por padrão, o Visual Studio detecta uma marca de ordem de byte para determinar se o arquivo de origem está em um formato Unicode codificado, por exemplo, UTF-16 ou UTF-8. Se nenhuma marca de ordem de byte for encontrada, ele assume que o arquivo de origem está codificado usando a página de código do usuário atual, a menos que você especifique um nome de conjunto de caracteres ou página de código usando a opção / source-charset.
A codificação UTF-8 de äöüß
é C3 A4 C3 B6 C3 BC C3 9F
e, portanto, a linha:
std::wstring wstr = L"äöüß";
é visto pelo compilador como:
std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;
Supondo que a página de código ativa seja o Windows-1252 usual , os caracteres (estendidos) mapeiam como:
win-1252 char unicode
\xC3 Ã U+00C3
\xA4 ¤ U+00A4
\xB6 ¶ U+00B6
\xBC ¼ U+00BC
\x9F Ÿ U+0178
Portanto, L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"
é traduzido para:
std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;
Para evitar essa tradução (incorreta), o Visual C ++ precisa ser informado de que o arquivo de origem está codificado como UTF-8, passando uma opção explícita /source-charset:utf-8
(ou /utf-8) do compilador. Para projetos baseados em CMake, isso pode ser feito usando add_compile_options
como mostrado em Possível forçar o CMake / MSVC a usar a codificação UTF-8 para arquivos de origem sem um BOM? C4819 .
portanto, seria de se esperar que wstr (a variável std :: wstring) fosse (qualquer tipo de) codificado em UTF-16
std::wstring
não especifica uma codificação. É uma sequência de "caracteres largos", para algum tipo de caracteres largos (que são definidos pela implementação).
Existem facetas de conversão definidas na biblioteca padrão para a conversão de / para codificações diferentes.