2012-03-25 14 views
11

Actualmente estoy escribiendo una aplicación que me exige llamar a GetWindowText en ventanas arbitrarias y almacenar esos datos en un archivo para su posterior procesamiento. Para resumir, he notado que mi herramienta estaba fallando en Battlefield 3, y reducido el problema con el carácter siguiente en su título de la ventana: http://www.fileformat.info/info/unicode/char/2122/index.htmWindows Unicode C++ Stream Output Failure

así que creé una aplicación de prueba pequeña, que sólo hace lo siguiente:

std::wcout << L"\u2122"; 

Bajo y he aquí que interrumpe la salida a la ventana de la consola durante el resto del programa.

¿Por qué MSVC STL se atraganta con este carácter (y supongo que otros) cuando las API como MessageBoxW etc. lo muestran bien?

¿Cómo puedo obtener esos caracteres impresos en mi archivo?

Probado tanto en VC10 como en VC11 con Windows 7 x64.

Lo siento por la publicación mal construida, me estoy arrancando el pelo aquí.

Gracias.

EDIT:

caso de prueba Mínimo

#include <fstream> 
#include <iostream> 

int main() 
{ 
    { 
    std::wofstream test_file("test.txt"); 
    test_file << L"\u2122"; 
    } 

    std::wcout << L"\u2122"; 
} 

Resultado esperado: Carácter '™' impresa a la consola y el archivo. Resultado observado: el archivo se crea pero está vacío. Sin salida a la consola.

me han confirmado que el tipo de letra I "m usando para mi consola es capaz de mostrar el carácter en cuestión, y el archivo es definitivamente vacío (0 bytes de tamaño)

EDIT:.

Más depuración muestra que el 'failbit' y 'badbit' se establecen en la corriente (s)

EDIT:.

también he intentado usar Boost.Locale y estoy teniendo el mismo problema incluso con la nueva configuración regional imbuido de manera global y explícita para todos corrientes de ard.

Respuesta

14

Para escribir en un archivo, tiene que establecer la configuración regional correcta, por ejemplo, si desea escribir como caracteres UTF-8, hay que añadir

const std::locale utf8_locale 
      = std::locale(std::locale(), new std::codecvt_utf8<wchar_t>()); 
test_file.imbue(utf8_locale); 

Hay que añadir estos 2 incluyen archivos

#include <codecvt> 
#include <locale> 

escribir en la consola tiene que configurar la consola en el modo correcto (esto es ventanas específica) mediante la adición de

_setmode(_fileno(stdout), _O_U8TEXT); 

(en caso de que desee usar UTF-8).

Para ello hay que añadir estos 2 archivos de inclusión:

#include <fcntl.h> 
#include <io.h> 

Además usted tiene que asegurarse de que su está utilizando una fuente compatible con Unicode (como por ejemplo Lucida Console). Puede cambiar la fuente en las propiedades de la ventana de su consola.

El programa completo ahora se ve así:

#include <fstream> 
#include <iostream> 
#include <codecvt> 
#include <locale> 
#include <fcntl.h> 
#include <io.h> 

int main() 
{ 

    const std::locale utf8_locale = std::locale(std::locale(), 
            new std::codecvt_utf8<wchar_t>()); 
    { 
    std::wofstream test_file("c:\\temp\\test.txt"); 
    test_file.imbue(utf8_locale); 
    test_file << L"\u2122"; 
    } 

    _setmode(_fileno(stdout), _O_U8TEXT); 
    std::wcout << L"\u2122"; 
} 
+1

Bueno, voy a estar maldito, imbuir que la configuración UTF8 realmente funcionó ... Ahora ¿por qué diablos no es Boost.Locale haciendo eso por mí?Interpreté los documentos diciendo que se supone que UTF-8 es la codificación estrecha predeterminada, y he imbuido la configuración regional globalmente y a todas las transmisiones estáticas, así que qué demonios ... – RaptorFactor

2

¿Siempre está usando std::wcout o está a veces usando std::cout? Mezclar esto no funcionará. Por supuesto, la descripción del error "asfixia" no dice en absoluto qué problema está observando. Sin embargo, sospecho que este es un problema diferente al de los archivos.

Como no hay una descripción real del problema, se necesita un poco de una bola de cristal seguida de un disparo en la oscuridad para solucionar el problema ... Dado que desea obtener caracteres Unicode del archivo, asegúrese de que la secuencia de archivos está usando un std::locale cuya faceta std::codecvt<...> realmente convierte a una codificación Unicode adecuada.

+0

Siempre estoy usando amplios tipos y apis. Incluso algo tan simple como la línea que publiqué en mi pregunta falla en mi plataforma. Lo mismo si reemplaza wcout con un wofstream. – RaptorFactor

+0

He agregado un caso de prueba mínimo. – RaptorFactor

+0

¿Ha verificado que el 'std :: codecvt ' utilizado por el predeterminado 'std :: locale' utiliza una codificación consciente de Unicode? Boost parece tener una [faceta UTF-8] (http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/codecvt.html). Sospecho que 'std :: wcout' en su plataforma utiliza' std :: basic_filebuf ', es decir, funcionaría para ambos archivos y para la salida de consok. –

2

Acabo de probar GCC (versiones 4.4 a 4.7) y MSVC 10, que presentan este problema.

Igualmente roto es wprintf, que hace tan poco como la API de secuencia de C++.

También he probado la API de Win32 prima para ver si nada más estaba causando el fracaso, y esto funciona:

#include <windows.h> 
int main() 
{ 
    HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE); 
    DWORD n; 
    WriteConsoleW(stdout, L"\u03B2", 1, &n, NULL); 
} 

que escribe β a la consola (si se establece la fuente de cmd a algo así como Lucida Console) .

Conclusión: La salida wchar_t está terriblemente rota en las dos implementaciones de la biblioteca estándar grande de C++.

+2

No está terriblemente roto, solo horriblemente documentado. –

+0

¿Cuáles dirías que son mis opciones? Una reescritura para usar la API sin procesar implicaría miles de líneas de código. Boost.Locale tampoco pareció resolver el problema ... – RaptorFactor

+0

No tengo la biblioteca de Nicolai Josuttis ['The C++ Standard Library'] (http://www.josuttis.com/libbook/) a mano, pero es el libro definitivo sobre el tema. Y teniendo en cuenta que el bit IOStreams es co-escrito por Dietmar Kühl;), cubre bastante bien todo lo relacionado con la conversión de caracteres en IOStream. – MSalters

1

Aunque las secuencias de caracteres anchos toman Unicode como entrada, eso no es lo que producen como salida: los caracteres pasan por una conversión. Si un personaje no puede representarse en la codificación a la que está convirtiendo, el resultado falla.

+0

Parece tan "incorrecto" (por falta de una palabra mejor). No estoy seguro de que entiendo cómo realmente trabajar/arreglar lo que dices ... – RaptorFactor

+0

No creo que sea cierto, tampoco. 'std :: wstringstream' definitivamente es una secuencia de caracteres amplia (hereda de' std :: wstream'), pero no hace ninguna conversión. – MSalters