2012-03-25 14 views
11

Attualmente sto scrivendo un'applicazione che richiede di chiamare GetWindowText su finestre arbitrarie e di archiviare tali dati in un file per l'elaborazione successiva. Per farla breve, ho notato che il mio strumento stava venendo a mancare su Battlefield 3, e ho ristretto il problema verso il basso per il carattere seguente nella sua titolo della finestra: http://www.fileformat.info/info/unicode/char/2122/index.htmErrore uscita flusso Unicode C++ di Windows

così ho creato una piccola applicazione di prova, che fa proprio il seguente:

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

Basso ed ecco che interrompe l'output alla finestra della console per il resto del programma.

Perché MSVC STL si soffoca su questo personaggio (e ne assumo altri) quando le API come MessageBoxW ecc lo visualizzano correttamente?

Come posso stampare quei caratteri sul mio file?

Testato su VC10 e VC11 su Windows 7 x64.

Scusate per il post mal costruito, mi sto strappando i capelli.

Grazie.

EDIT:

Minimal banco di prova

#include <fstream> 
#include <iostream> 

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

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

Risultato atteso: '™' carattere stampato a consolare e file. Risultato osservato: il file viene creato ma vuoto. Nessuna uscita per console.

mi hanno confermato che il tipo di carattere I "m usando per la mia console è in grado di visualizzare il personaggio in questione, e il file è decisamente vuoto (0 byte di dimensione)

EDIT:.

Ulteriori debug mostra che la 'failbit' e 'badbit' sono impostate nel flusso (s)

EDIT:.

ho anche provato con Boost.Locale e sto avendo lo stesso problema anche con il nuovo locale permeato globalmente ed esplicitamente a tutti flussi ardenti.

risposta

14

Per scrivere in un file, è necessario impostare correttamente il locale, ad esempio se si desidera scrivere loro come caratteri UTF-8, è necessario aggiungere

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

È necessario aggiungere questi 2 includono file

#include <codecvt> 
#include <locale> 

per scrivere alla console è necessario impostare la console in modalità corretta (questo è Windows specifico) con l'aggiunta di

_setmode(_fileno(stdout), _O_U8TEXT); 

(nel caso in cui si desideri utilizzare UTF-8).

Per questo si deve aggiungere questi 2 file include:

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

Inoltre è necessario fare in modo che si sta usando un tipo di carattere che supporta Unicode (come ad esempio Lucida Console). È possibile modificare il carattere nelle proprietà della finestra della console.

Il programma completo appare come segue:

#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

Beh, sarei dannato, perché l'ambientazione UTF8 funzionava davvero ... Ora perché diavolo non è Boost.Locale a farlo per me?Ho interpretato i documenti dicendo che l'UTF-8 è considerata la codifica ristretta predefinita, e ho imbustato le impostazioni internazionali a tutti i flussi statici, quindi che diavolo ... – RaptorFactor

2

Usi sempre std::wcout o a volte usi std::cout? Mescolare questi non funzionerà. Naturalmente, la descrizione dell'errore "soffocamento" non dice affatto quale problema si sta osservando. Sospetto che si tratti di un problema diverso da quello che utilizza i file, comunque.

Poiché non esiste una descrizione reale del problema, ci vuole un po 'di una sfera di cristallo seguita da uno sparo nell'oscurità per colpire il problema ... Dal momento che si desidera ottenere i caratteri Unicode dal file assicurarsi che il flusso di file stai usando usa un std::locale il cui faccetto std::codecvt<...> in realtà converte in una codifica Unicode adatta.

+0

Utilizzo sempre ampi tipi e API. Anche qualcosa di semplice come la linea che ho postato nella mia domanda non riesce sulla mia piattaforma. Idem se sostituisci wcout con un wofstream. – RaptorFactor

+0

Ho aggiunto un caso di test minimo. – RaptorFactor

+0

Hai verificato che 'std :: codecvt ' usato da default 'std :: locale' utilizza una codifica a conoscenza di Unicode? Boost sembra avere un [UTF-8 facet] (http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/codecvt.html). Sospetto che 'std :: wcout' sulla tua piattaforma usi un' std :: basic_filebuf 'vale a dire che funzionerebbe per entrambi i file e conseggerà l'output. –

2

Ho appena testato GCC (versioni 4.4 fino a 4.7) e MSVC 10, che presentano tutti questo problema.

Altrettanto rotto è wprintf, che funziona come l'API del flusso C++.

ho testato anche l'API Win32 grezzo per vedere se non altro è stata la causa del fallimento, e questo funziona:

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

Quali scrive β alla console (se si imposta carattere di cmd per qualcosa di simile a Lucida Console) .

Conclusione: l'output di wchar_t è orribilmente rotto in entrambe le grandi implementazioni di libreria C++ standard.

+2

Non è orribilmente rotto, solo orribilmente documentato. –

+0

Cosa diresti che le mie opzioni sono? Una riscrittura per utilizzare l'API raw implicherebbe migliaia di righe di codice. Boost.Locale non sembra aver risolto il problema ... – RaptorFactor

+0

Non ho Nicolai Josuttis '['The C++ Standard Library'] (http://www.josuttis.com/libbook/) a portata di mano, ma è il libro definito sull'argomento. E considerando che il bit IOStreams è co-scritto da Dietmar Kühl;), copre abbastanza bene l'intero processo di conversione dei personaggi in IOStream. – MSalters

1

Anche se i flussi di caratteri ampi accettano Unicode come input, non è ciò che producono come output: i caratteri passano attraverso una conversione. Se un carattere non può essere rappresentato nella codifica in cui si sta convertendo, l'output fallisce.

+0

Sembra così "sbagliato" (per mancanza di una parola migliore). Non sono sicuro di capire come effettivamente funzionare/correggere quello che stai dicendo però ... – RaptorFactor

+0

Non penso che sia vero, neanche. 'std :: wstringstream' è sicuramente un ampio flusso di caratteri (ereditato da' std :: wstream'), ma non esegue alcuna conversione. – MSalters