¿Cómo se codifica const std :: wstring y cómo cambiar a UTF-16?
He creado este mínimo de trabajo C ++ fragmento de ejemplo para comparar bytes (por su representación hexadecimal) en una std::string
y una std::wstring
hora de definir una cadena con caracteres no ASCII alemán en uno u otro tipo.
#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;
}
El resultado de este fragmento es
c3 a4 c3 b6 c3 bc c3 9f
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178
Ejecuté esto en una PC con Windows 10 64-bit Pro , compilando con MSVC 2019 Community Edition en la versión 16.8.1, usando el sistema de compilación cmake con lo siguienteCMakeLists.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)
Leí que los std::string
mensajes de correo electrónico se basan en el char
tipo, que es un solo byte. Veo que la salida de mi fragmento indica que str
(la std::string
variable) está codificada en UTF-8 . Leí que los compiladores de Microsoft usan wchar_t
s con 2 bytes para componer std::wstring
s (en lugar de 4 bytes wchar_t
por ej. GNU gcc) y por lo tanto esperarían que wstr
(la std::wstring
variable) esté (cualquier tipo de) codificada en UTF-16 . Pero no puedo entender por qué la "ß" (s aguda latina) está codificada como 0x00c30178
esperaba 0x00df
. Que alguien me diga:
- ¿Por qué está pasando esto?
- ¿Cómo puedo terminar con
std::wstring
s codificados en UTF-16 (Big Endian estaría bien, no me importa una lista de materiales)? ¿Probablemente necesito decirle al compilador de alguna manera? - ¿Qué tipo de codificación es esta?
EDITAR 1
cambió el título, ya que no se ajustaba correctamente a las preguntas (y en realidad UTF-8 y UTF-16 son codificaciones diferentes, por lo que yo mismo conozco la respuesta ya ...)
EDITAR 2
olvidé mencionar: uso el amd64
destino del compilador mencionado
EDITAR 3
si agrego la /utf-8
bandera como se señala en los comentarios de dxiv (vea su SO-Post vinculado ), obtengo el resultado deseado
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df
que se parece a UTF-16-BE (sin BOM) para mí. Como tuve problemas con el orden correcto de los comandos de cmake, este es mi CmakeLists.txt
archivo actual . Es importante poner el add_compile_options
comando antes del add_executable
comando (agregué el Aviso por conveniencia)
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)
Encuentro la if-endif
forma más legible que la de sintaxis del generador, pero la escritura también funcionaría.add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
Nota: Para Qt-Projects hay un buen cambio para el .pro
archivo (vea esta publicación de Qt-Form )
win32 {
QMAKE_CXXFLAGS += /utf-8
}
Aún así, la primera parte de mi pregunta está abierta: ¿Qué codificación es 0x00c30178
para "ß" (s sostenida latina)?
Respuestas
Como se aclara en los comentarios, el .cpp
archivo fuente está codificado en UTF-8. Sin una lista de materiales y sin un modificador explícito /source-charset:utf-8
, el compilador de Visual C ++ adopta de forma predeterminada que el archivo de origen se guarda en la codificación de la página de códigos activa. De la documentación de Set Source Character Set :
De forma predeterminada, Visual Studio detecta una marca de orden de bytes para determinar si el archivo de origen está en un formato Unicode codificado, por ejemplo, UTF-16 o UTF-8. Si no se encuentra ninguna marca de orden de bytes, se supone que el archivo fuente está codificado usando la página de códigos de usuario actual, a menos que especifique un nombre de juego de caracteres o una página de códigos usando la opción / source-charset.
La codificación UTF-8 de äöüß
es C3 A4 C3 B6 C3 BC C3 9F
, y por lo tanto la línea:
std::wstring wstr = L"äöüß";
es visto por el compilador como:
std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;
Suponiendo que la página de códigos activa sea el Windows-1252 habitual , los caracteres (extendidos) se asignan como:
win-1252 char unicode
\xC3 Ã U+00C3
\xA4 ¤ U+00A4
\xB6 ¶ U+00B6
\xBC ¼ U+00BC
\x9F Ÿ U+0178
Por lo tanto, L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"
se traduce a:
std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;
Para evitar dicha (mala) traducción, Visual C ++ necesita que se le diga que el archivo fuente está codificado como UTF-8 pasando un modificador explícito /source-charset:utf-8
(o /utf-8) del compilador. Para proyectos basados en CMake, esto se puede hacer usando add_compile_options
como se muestra en ¿Es posible forzar a CMake / MSVC a usar la codificación UTF-8 para archivos de origen sin una lista de materiales? C4819 .
por lo tanto, esperaría que wstr (la variable std :: wstring) tenga (cualquier tipo de) codificación UTF-16
std::wstring
no especifica una codificación. Es una secuencia de "caracteres anchos", para algún tipo de caracteres anchos (que están definidos por la implementación).
Hay facetas de conversión definidas en la biblioteca estándar para convertir a / desde diferentes codificaciones.