Solución:
Por fin, lo tengo funcionando. Esta respuesta combina la información de Miles Budnek, Paul y mkluwe con algunas investigaciones propias. Primero, déjame empezar con código que funcionará en Windows 10. Después de eso, lo guiaré a través del código y le explicaré por qué no funcionará de inmediato en Windows 7.
#include <string>
#include <iostream>
#include <Windows.h>
#include <cstdio>
int main() {
// Set console code page to UTF-8 so console known how to interpret string data
SetConsoleOutputCP(CP_UTF8);
// Enable buffering to prevent VS from chopping up UTF-8 byte sequences
setvbuf(stdout, nullptr, _IOFBF, 1000);
std::string test = u8"Greek: αβγδ; German: Übergrößenträger";
std::cout << test << std::endl;
}
El código comienza configurando la página de códigos, como sugiere Miles Budnik. Esto le dirá a la consola que interprete el flujo de bytes que recibe como UTF-8, no como una variación de ANSI.
A continuación, hay un problema en el código STL que viene con Visual Studio. std::cout
imprime sus datos en un búfer de flujo de tipo std::basic_filebuf
. Cuando ese búfer recibe una cadena (a través de std::basic_streambuf::sputn()
), no lo pasará al archivo subyacente como un todo. En cambio, pasará cada byte por separado. Como explica mkluwe, si la consola recibe una secuencia de bytes UTF-8 como bytes individuales, no los interpretará como un solo punto de código. En cambio, los tratará como varios personajes. Cada byte dentro de una secuencia de bytes UTF-8 es un punto de código no válido por sí solo, por lo que verá en su lugar. Hay un informe de error relacionado para Visual Studio, pero se cerró como Por diseño. La solución alternativa es habilitar el almacenamiento en búfer para la transmisión. Como beneficio adicional, eso le dará un mejor rendimiento. Sin embargo, es posible que ahora necesite vaciar el flujo con regularidad como lo hago con std::endl
, o es posible que su salida no se muestre.
Por último, la consola de Windows admite fuentes de trama y fuentes TrueType. Como señaló Paul, las fuentes ráster simplemente ignorarán la página de códigos de la consola. Por lo tanto, los caracteres Unicode que no sean ASCII solo funcionarán si la consola está configurada con una fuente TrueType. Hasta Windows 7, la fuente predeterminada es de trama, por lo que el usuario tendrá que cambiarla manualmente. Afortunadamente, Windows 10 cambia la fuente predeterminada a Consolas, por lo que esta parte del problema debería resolverse con el tiempo.
El problema no es std::cout
pero la consola de Windows. Usando C-stdio obtendrá el ü
con fputs( "xc3xbc", stdout );
después de configurar la página de códigos UTF-8 (ya sea usando SetConsoleOutputCP
o chcp
) y configurar una fuente compatible con Unicode en la configuración de cmd (Consolas debería admitir más de 2000 caracteres y hay trucos de registro para agregar fuentes más capaces a cmd).
Si genera un byte tras otro con putc('xc3'); putc('xbc');
obtendrá el doble tofu ya que la consola los interpreta por separado como caracteres ilegales. Esto es probablemente lo que hacen las secuencias de C ++.
Vea la salida UTF-8 en la consola de Windows para una discusión extensa.
Para mi propio proyecto, finalmente implementé un std::stringbuf
haciendo la conversión a Windows-1252. Si realmente necesita una salida Unicode completa, esto realmente no lo ayudará, sin embargo.
Un enfoque alternativo sería sobrescribir cout
streambuf, usando fputs
para la salida real:
#include <iostream>
#include <sstream>
#include <Windows.h>
class MBuf: public std::stringbuf {
public:
int sync() {
fputs( str().c_str(), stdout );
str( "" );
return 0;
}
};
int main() {
SetConsoleOutputCP( CP_UTF8 );
setvbuf( stdout, nullptr, _IONBF, 0 );
MBuf buf;
std::cout.rdbuf( &buf );
std::cout << u8"Greek: αβγδn" << std::flush;
}
Apagué el búfer de salida aquí para evitar que interfiera con secuencias de bytes UTF-8 sin terminar.
std::cout
está haciendo exactamente lo que debería: está enviando su texto codificado en UTF-8 a la consola, pero su consola interpretará esos bytes usando su página de códigos actual. Debe configurar la consola de su programa en la página de códigos UTF-8:
#include <string>
#include <iostream>
#include <Windows.h>
int main() {
std::string test = u8"Greek: αβγδ; German: Übergrößenträger";
SetConsoleOutputCP(CP_UTF8);
std::cout << test;
}
Sería genial si Windows cambiara la página de códigos predeterminada a UTF-8, pero es probable que no pueda debido a problemas de compatibilidad con versiones anteriores.