2010-02-23 12 views
14

Attualmente sto lavorando a un programma MFC che deve funzionare specificamente con UTF-8. Ad un certo punto, devo scrivere i dati UTF-8 in un file; per farlo, sto usando CFiles e CStrings.UTF-8, CString e CFile? (C++, MFC)

Quando arrivo di scrivere utf-8 (caratteri russi, per essere più precisi) dati in un file, l'output si presenta come

Ðàñïå÷àòàíî: 
Ñèñòåìà 
Ïðîèçâîäñòâî 

e ecc Questo non è assurely utf-8. Per leggere correttamente questi dati, devo modificare le mie impostazioni di sistema; cambiare i caratteri non ASCII in una tabella di codifica russa funziona, ma poi tutti i miei caratteri non-ascii basati su latino falliscono. Ad ogni modo, è così che lo faccio.

CFile CSVFile(m_sCible, CFile::modeCreate|CFile::modeWrite); 
CString sWorkingLine; 
//Add stuff into sWorkingline 
CSVFile.Write(sWorkingLine,sWorkingLine.GetLength()); 
//Clean sWorkingline and start over 

Mi manca qualcosa? Devo usare qualcos'altro, invece? C'è una specie di cattura che ho perso? Sarò sintonizzato per la tua saggezza ed esperienza, colleghi programmatori.

MODIFICA: Naturalmente, come ho appena fatto una domanda, ho finalmente trovato qualcosa che potrebbe essere interessante, che può essere trovato here. Ho pensato che potrei condividerlo.

EDIT 2:

Va bene, così ho aggiunto la distinta al mio file, che ora contiene il carattere chineese, probabilmente perché non ho convertire la mia linea in UTF-8. Per aggiungere il bom ho fatto ...

char BOM[3]={0xEF, 0xBB, 0xBF}; 
CSVFile.Write(BOM,3); 

E dopo questo, ho aggiunto ...

TCHAR TestLine; 
    //Convert the line to UTF-8 multibyte. 
    WideCharToMultiByte (CP_UTF8,0,sWorkingLine,sWorkingLine.GetLength(),TestLine,strlen(TestLine)+1,NULL,NULL); 
    //Add the line to file. 
    CSVFile.Write(TestLine,strlen(TestLine)+1); 

Ma poi non riesco a compilare, come io non so davvero come ottenere il lunghezza di TestLine. strlen non sembra accettare TCHAR. Risolto, utilizzato invece una lunghezza statica di 1000.

EDIT 3:

Così, ho aggiunto questo codice ...

wchar_t NewLine[1000]; 
    wcscpy(NewLine, CT2CW((LPCTSTR) sWorkingLine)); 
    TCHAR* TCHARBuf = new TCHAR[1000]; 

    //Convert the line to UTF-8 multibyte. 
    WideCharToMultiByte (CP_UTF8,0,NewLine,1000,TCHARBuf,1000,NULL,NULL); 

    //Find how many characters we have to add 
    size_t size = 0; 
    HRESULT hr = StringCchLength(TCHARBuf, MAX_PATH, &size); 

    //Add the line to the file 
    CSVFile.Write(TCHARBuf,size); 

Si compila bene, ma quando vado sguardo al mio nuovo file, è esattamente la stessa di quando ho didn' Ho tutto questo nuovo codice (es: Ðàñïå ÷ àòàíî :). Mi sembra di non aver fatto un passo avanti, anche se immagino che solo una piccola cosa sia ciò che mi separa dalla vittoria.

EDIT 4:

ho rimosso in precedenza codice aggiunto, come ha chiesto Nate, e ho deciso di utilizzare il suo codice, invece, il che significa che ora, quando arrivo a aggiungere la mia linea, ho ...

 CT2CA outputString(sWorkingLine, CP_UTF8); 

    //Add line to file. 
    CSVFile.Write(outputString,::strlen(outputString)); 

Tutto si compila bene, ma i caratteri russi sono mostrati come ???????. Avvicinarsi, ma ancora non quello. Btw, vorrei ringraziare tutti coloro che hanno provato/cercano di aiutarmi, è MOLTO apprezzato. Sono stato bloccato su questo per un po 'di tempo, non vedo l'ora che questo problema sparisca.

FINAL EDIT (spero) Cambiando il modo in cui ho ottenuto i miei caratteri UTF-8 (reencoded senza sapere veramente), che era errato con il mio nuovo modo di emettere il testo, ho ottenuto risultati accettabili.Aggiungendo il carattere BOM UTF-8 all'inizio del mio file, potrebbe essere letto come Unicode in altri programmi, come Excel.

Evviva! Grazie a tutti!

+1

Avrete bisogno di usare _tcslen per ottenere la lunghezza di una stringa TCHAR. Come: \t TCHAR * testTCHAR = _T ("test"); \t int tcharLength = _tcslen (testTCHAR); –

+1

Anche se è necessario convertire un CString in una stringa TCHAR *, provare questo \t CString testCString = _T ("test"); \t TCHAR * testTCHAR = testCString.GetBuffer(); –

+0

Stai lavorando troppo duramente. Usa 'CT2CA' con un secondo parametro di' CP_UTF8'. Vedi il mio post qui sotto. – Nate

risposta

24

Quando i dati di uscita è necessario fare (questo presuppone che si sta compilando in modalità Unicode, che è altamente raccomandato):

CString russianText = L"Привет мир"; 

CFile yourFile(_T("yourfile.txt"), CFile::modeWrite | CFile::modeCreate); 

CT2CA outputString(russianText, CP_UTF8); 
yourFile.Write(outputString, ::strlen(outputString)); 

Se _UNICODE non è definito (si sta lavorando in modalità multi-byte invece), è necessario sapere in quale codice è inserito il testo di input e convertirlo in qualcosa che è possibile utilizzare. Questo esempio mostra lavorando con testo russo che è in formato UTF-16, salvandola in UTF-8:

// Example 1: convert from Russian text in UTF-16 (note the "L" 
// in front of the string), into UTF-8. 
CW2A russianTextAsUtf8(L"Привет мир", CP_UTF8); 
yourFile.Write(russianTextAsUtf8, ::strlen(russianTextAsUtf8)); 

Più probabilmente, il testo russo è in qualche altra pagina di codice, come ad esempio KOI-8R. In tal caso, è necessario convertire dall'altra tabella codici in UTF-16. Quindi convertire UTF-16 in UTF-8. Non è possibile convertire direttamente da KOI-8R a UTF-8 utilizzando le macro di conversione perché cercano sempre di convertire il testo stretto nella codepage di sistema. Quindi il modo più semplice è quello di fare questo:

// Example 2: convert from Russian text in KOI-8R (code page 20866) 
// to UTF-16, and then to UTF-8. Conversions between UTFs are 
// lossless. 
CA2W russianTextAsUtf16("\xf0\xd2\xc9\xd7\xc5\xd4 \xcd\xc9\xd2", 20866); 
CW2A russianTextAsUtf8(russianTextAsUtf16, CP_UTF8); 
yourFile.Write(russianTextAsUtf8, ::strlen(russianTextAsUtf8)); 

non hai bisogno di una distinta base (è facoltativo; io non lo uso a meno che non ci fosse un motivo specifico per farlo).

Assicurati di leggere questo: http://msdn.microsoft.com/en-us/library/87zae4a3(VS.80).aspx. Se si utilizza erroneamente CT2CA (ad esempio, utilizzando l'operatore di assegnazione) si incontreranno dei problemi. La pagina della documentazione collegata mostra esempi di come usare e come non usarlo.

Per ulteriori informazioni:

  • Il C in CT2CA indica const. Lo utilizzo quando possibile, ma alcune conversioni supportano solo la versione non const (ad esempio CW2A).
  • Il T in CT2CA indica che si sta convertendo da un LPCTSTR. Quindi funzionerà se il tuo codice è compilato con il flag _UNICODE o no. È inoltre possibile utilizzare CW2A (dove W indica caratteri larghi).
  • Il Un in CT2CA indica che si sta convertendo a un "ANSI" (char 8 bit) stringa.
  • Infine, il secondo parametro su CT2CA indica la tabella codici in cui si sta eseguendo la conversione.

per eseguire la conversione inversa (da UTF-8 a LPCTSTR), si potrebbe fare:

CString myString(CA2CT(russianText, CP_UTF8)); 

In questo caso, stiamo convertendo da una stringa "ANSI" in UTF-8 formato, a un LPCTSTR. Si assume sempre che LPCTSTR sia UTF-16 (se è definito _UNICODE) o la tabella codici di sistema corrente (se _UNICODE non è definito).

+0

Ho provato quello che hai detto: ho rimosso il BOM e ho cambiato il mio codice per il tuo. Ora, i personaggi sono rappresentati come ??????? ??. Qualcosa manca ancora, forse? Posterò una modifica – SeargX

+0

Rappresentati come punti interrogativi dove? Guarda il file risultante usando un editor esadecimale. Dovresti vedere qualcosa come [questo] (http://i.imgur.com/RcUsh.png). E se lo apri nel Blocco note, dovresti vedere [questo] (http://imgur.com/Yl3OU.png). In caso contrario, il testo originale probabilmente non è codificato correttamente. Spero che tu stia usando la definizione '_UNICODE' e il tuo input è UTF-16. In caso contrario, è necessario utilizzare i macro per convertire da qualsiasi tabella codici il testo originale è in, alla tabella codici desiderata. – Nate

+0

I punti interrogativi si trovano nel file risultante e hanno tutti un codice esadecimale con il punto interrogativo (3F, credo). Non sto usando la definizione _UNICODE, e non penso che sarebbe una buona idea. I caratteri russi che ho letto provengono da un file XML, che apro con tinyXML, che non supporta le pagine di codifica UTF-16, solo UTF-8 e Latin 1. Credo di dover usare i macro, anche se non ho familiarità con loro. – SeargX

6

Dovrai convertire sWorkingLine in UTF-8 e poi scriverlo nel file.

WideCharToMultiByte può convertire stringhe unicode in UTF-8 se si seleziona la codepage CP_UTF8. MultiByteToWideChar può convertire caratteri ASCII in Unicode.

+0

Usando tale funzione, tutto il testo incluso sarà cambiato in più di un byte, o solo i caratteri non ascii? – SeargX

+0

@SeargX, solo il non-ascii se si utilizza UTF-8. –

+0

D: Perfetto, grazie. @Everyone In quale tipo di stringa devo inserire i miei dati convertiti? TCHAR? Come posso determinare la lunghezza della linea, che è necessaria nella funzione multibytetowidechar? – SeargX

0

Assicurati di utilizzare Unicode (TCHAR è wchar_t). Quindi, prima di scrivere i dati, convertirli utilizzando la funzione API Win32 WideCharToMultiByte.

+0

'wchar_t' NON è ** UTF-8 **, è ** UCS-2 **. – rhavin