2015-09-01 6 views
5

Sommario: Cosa devo fare per stampare correttamente una stringa letterale definita nel codice sorgente che è stato memorizzato nella codifica UTF-8 (Windows CP 65001) su una console cmd utilizzando lo streaming std::cout?C++ 11 std :: cout << "stringa letterale in UTF-8" alla console cmd di Windows? (Visual Studio 2015)

Motivazione: Vorrei modificare l'eccellente Catch unit-testing framework (come un esperimento) in modo che visualizzerebbe my texts con caratteri accentati. La modifica dovrebbe essere semplice, affidabile e dovrebbe essere anche utile per altre lingue e ambienti di lavoro in modo che possa essere accettata dall'autore come un miglioramento. O se conosci Catch e se c'è qualche soluzione alternativa, potresti postarla?

Dettagli: Cominciamo con la versione ceca del "quick brown fox ..."

#include <iostream> 
#include "windows.h" 

using namespace std; 

int main() 
{ 
    cout << "\n-------------------------- default cmd encoding = 852 -------------------\n"; 
    cout << "Příšerně žluťoučký kůň úpěl ďábelské ódy!" << endl; 

    cout << "\n-------- Windows Central European (1250) set for the cmd console --------\n"; 
    SetConsoleOutputCP(1250); 
    std::cout << "Příšerně žluťoučký kůň úpěl ďábelské ódy!" << std::endl; 

    cout << "\n------------- Windows UTF-8 (65001) set for the cmd console -------------\n"; 
    SetConsoleOutputCP(CP_UTF8); 
    std::cout << "Příšerně žluťoučký kůň úpěl ďábelské ódy!" << std::endl; 
} 

stampa il seguente (tipo di carattere impostato a Lucida Console): enter image description here

Il cmd la codifica predefinita è 852, la codifica Windows predefinita è 1250 e il codice sorgente è stato salvato utilizzando la codifica 65001 (UTF-8 con BOM). Lo SetConsoleOutputCP(1250); modifica la codifica cmd (programmaticamente) allo stesso modo di chcp 1250.

Osservazione: Quando si imposta la codifica 1250, il valore letterale stringa UTF-8 viene stampato correttamente. Credo che possa essere spiegato, ma è davvero strano. C'è qualche decente, umano, modo generale per risolvere il problema?

Update: Il "narrow string literal" sono memorizzati utilizzando la codifica di Windows-1250 nel mio caso (la codifica di Windows nativo per centrale europea). Sembra essere indipendente dalla codifica del codice sorgente. Il compilatore lo salva nella codifica originale delle finestre . Per questo motivo, il passaggio da cmd a quella codifica fornisce l'output desiderato. È uggly, ma come posso ottenere le finestre native che codificano in modo programmatico (per passarlo allo SetConsoleOutputCP(cpX))? Ciò di cui ho bisogno è una costante valida per la macchina in cui è avvenuta la compilazione. Non dovrebbe essere una codifica nativa per la macchina su cui viene eseguito l'eseguibile.

Il C++ 11 introdotto anche u8"the UTF-8 string literal", ma non sembra adattarsi con SetConsoleOutputCP(CP_UTF8);

+1

possibilmente correlate: http://stackoverflow.com/questions/18904081/printing-unicode-characters-c/18906295#18906295 – luk32

+0

@ luk32: Grazie per i riferimenti. Lo guarderò. – pepr

+1

Quando si compila un sorgente UTF-8 in MSVC, converte i valori letterali stringa in codifica nativa se il file inizia con _UTF-8 BOM_. Quando lo rimuovi, la stringa di test dovrebbe essere stampata correttamente nel terzo caso. – Melebius

risposta

2

Questa è una risposta parziale trovata tramite salti il ​​collegamento da luk32 e confermando i commenti Melebius (vedi sotto la questione). Questa non è la risposta completa e sarò felice di accettare il tuo commento di follow-up.

Ho appena trovato il UTF-8 Everywhere Manifesto che tocca il problema. Il punto 17. Q: How do I write UTF-8 string literal in my C++ code? dice (anche esplicito per Microsoft C++):

Tuttavia il modo più semplice è quello di scrivere solo la stringa così com'è e salvare il file sorgente codificato in UTF-8:

       "∃y ∀x ¬(x ≺ y)" 

Sfortunatamente, MSVC lo converte in qualche codepage ANSI, corrompendo la stringa.Per ovviare al problema, salvare il file in UTF-8 senza BOM. MSVC presumerà che sia nella codepage corretta e non tocchi le stringhe. Tuttavia, rende impossibile l'uso di identificatori Unicode e letterali di stringa ampia (che non verranno comunque utilizzati).

Mi piace molto il manifesto. Per farla breve, usando parolacce, e forse semplicistica, si dice:

Ignora la wstring, wchar_t, e le cose come. Ignora le codepage. Ignora i prefissi letterali stringa come L, u, U, u8. Usa UTF-8 ovunque. Scrivi tutti i valori letterali "naturally". Assicurati che sia anche memorizzato nel file binario compilato.

Se il codice seguente viene memorizzato con UTF-8 senza BOM ...

#include <iomanip> 
#include <iostream> 
#include "windows.h" 

using namespace std; 

int main() 
{ 
    SetConsoleOutputCP(CP_UTF8); 
    cout << "Příšerně žluťoučký kůň úpěl ďábelské ódy!" << endl; 

    int cnt = 0; 
    for (unsigned int c : "Příšerně žluťoučký kůň úpěl ďábelské ódy!") 
    { 
     cout << hex << setw(2) << setfill('0') << (c & 0xff); 
     ++cnt; 
     if (cnt % 16 == 0)  cout << endl; 
     else if (cnt % 8 == 0) cout << " | "; 
     else if (cnt % 4 == 0) cout << " "; 
     else     cout << ' '; 
    } 
    cout << endl; 
} 

Esso stampa (dovrebbe essere codifica UTF-8) ...

enter image description here

Quando si salva l'origine come UTF-8 con BOM, viene stampato un risultato diverso ...

enter image description here

Tuttavia, il problema rimane: come impostare la codifica della console in modo programmatico in modo che la stringa UTF-8 venga stampata correttamente.

Mi sono arreso. La console cmd è semplicemente paralizzata e non vale la pena ripararla dall'esterno. Sto accettando il mio commento solo per chiudere la domanda. Se qualcuno trova una soluzione decente relativa al framework di test dell'unità Catch (potrebbe essere completamente diversa), sarò lieto di accettare il suo commento come risposta.

+1

Uso anche UTF-8 in questo modo per l'output di testi svedesi, funziona perfettamente con MSVC2015 a condizione che non ci sia BOM nel file .cpp.Nota: ** mai ** modificare il file utilizzando Blocco note, verrà creato un BOM. Usa Wordpad. –

+0

@HenrySkoglund: Grazie per il suggerimento. (Sto usando Notepad ++ per cose semplici.È possibile scegliere con o senza BOM anche in quell'editor,) Inviate il testo UTF-8 alla console 'cmd' tramite' std :: cout'? – pepr

0

Il compilatore MSVC tenta di codificare le stringhe const nel codice con la codifica locale. Nel tuo caso, utilizza code page 852. Quindi anche l'output del tuo cmd prova a leggere ed emettere la stringa con code page 1250, la stringa viene infatti memorizzata con code page 852. Tale incompatibilità tra memoria e lettura crea output errato.
Un modo per risolvere questo è memorizzare la stringa in un file codificato con code page 1250. Visual Studio Code fornisce tale funzionalità. È possibile leggere il file come file binario (cioè byte per byte) in un buffer di caratteri, quindi emettere il buffer.

char * memblock = new char[1024]; 
std::ifstream file("src.txt", std::ios::in | std::ios::binary | std::ios::ate); 
int size; 
if (file.is_open()) 
{ 
    size = file.tellg(); 
    memblock = new char[size]; 
    file.seekg(0, std::ios::beg); 
    file.read(memblock, size); 
    file.close(); 
} 
else 
{ 
    std::cout << "File not opened." << std::endl; 
} 
memblock[size] = 0; 
std::cout << memblock << std::endl; 

enter image description here

+0

Grazie, Fanny. Il problema è che ho bisogno di memorizzare il file in UTF-8 per altri motivi. – pepr

Problemi correlati