2010-02-23 17 views
14

Actualmente estoy trabajando en un programa MFC que específicamente tiene que trabajar con UTF-8. En algún momento, tengo que escribir datos UTF-8 en un archivo; para hacer eso, estoy usando CFiles y CStrings.UTF-8, CString y CFile? (C++, MFC)

Cuando llego a escribir UTF-8 (caracteres rusos, para ser más precisos) de datos en un archivo, la salida se ve como

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

y etc. Esto no es assurely UTF-8. Para leer estos datos correctamente, tengo que cambiar la configuración de mi sistema; cambiar los caracteres que no son ASCII a una tabla de codificación rusa funciona, pero luego todos mis caracteres latinos que no son ASCII fallan. De todos modos, así es como lo hago.

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

¿Falta algo? ¿Debo usar algo más en su lugar? ¿Hay algún tipo de captura que me haya perdido? Seré sintonizado por su sabiduría y experiencia, compañeros programadores.

EDIT: Por supuesto, como acabo de hacer una pregunta, finalmente encuentro algo que podría ser interesante, que se puede encontrar here. Pensé que podría compartirlo.

EDIT 2:

bien, así que añaden a la lista de materiales mi archivo, que ahora contiene caracteres chineese, probablemente porque no me convierto mi línea a UTF-8. Para añadir la lista de materiales que hice ...

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

Y después de eso, añadí ...

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); 

Pero entonces no puedo compilar, ya que no se sabe muy bien cómo sacar el longitud de TestLine. strlen no parece aceptar TCHAR. Solucionado, usó una longitud estática de 1000 en su lugar.

Datos 3:

Por lo tanto, he añadido el código ...

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); 

Se compila bien, pero cuando voy vistazo a mi nuevo archivo, que es exactamente lo mismo que cuando yo no' Tengo todo este código nuevo (por ejemplo: Ðàñïå ÷ àòàíî :). Siento que no di un paso adelante, aunque creo que solo una pequeña cosa es lo que me separa de la victoria.

EDITAR 4:

He quitado previamente añadido código, tal como pidió Nate, y decidió utilizar su código en su lugar, lo que significa que ahora, cuando llegue a añadir mi línea, no tengo ...

 CT2CA outputString(sWorkingLine, CP_UTF8); 

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

Todo compila bien, pero los caracteres rusos se muestran como ???????. Acercarse, pero aún no eso. Por cierto, me gustaría agradecer a todos los que intentaron/intentaron ayudarme, es MUCHO apreciado. He estado atrapado en esto por un tiempo, no puedo esperar a que este problema desaparezca. FINAL EDIT (Espero) Al cambiar la forma en que obtuve por primera vez mis caracteres UTF-8 (volví a codificar sin saber), lo cual era erróneo con mi nueva forma de mostrar el texto, obtuve resultados aceptables.Al agregar el carácter UTF-8 BOM al principio de mi archivo, podría leerse como Unicode en otros programas, como Excel.

¡Hurra! ¡Gracias a todos!

+1

Deberá usar _tcslen para obtener la longitud de una cadena TCHAR. Me gusta: \t TCHAR * testTCHAR = _T ("prueba"); \t int tcharLength = _tcslen (testTCHAR); –

+1

Además, si necesita convertir un CString en una cadena TCHAR * intente esto \t CString testCString = _T ("test"); \t TCHAR * testTCHAR = testCString.GetBuffer(); –

+0

Estás trabajando demasiado. Use 'CT2CA' con un segundo parámetro de' CP_UTF8'. Ver mi publicación a continuación. – Nate

Respuesta

24

Cuando los datos de salida que tiene que hacer (se asume que está compilando en modo Unicode, que es muy recomendable):

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

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

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

Si _UNICODE no está definido (que está trabajando en el modo multi-byte en vez), necesita saber en qué página de códigos está el texto de entrada y convertirlo en algo que pueda usar. Este ejemplo muestra cómo trabajar con texto ruso que está en formato UTF-16, guardándolo en 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)); 

Lo más probable, el texto ruso es de alguna otra página de códigos, tales como KOI-8E. En ese caso, debe convertir desde la otra página de códigos a UTF-16. Luego convierta el UTF-16 en UTF-8. No puede convertir directamente de KOI-8R a UTF-8 usando las macros de conversión porque siempre intentan convertir texto angosto a la página de códigos del sistema. Así que la forma más fácil es hacer esto:

// 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)); 

usted no necesita una lista de materiales (es opcional; yo no lo uso a menos que haya una razón específica para hacerlo).

Asegúrese de leer esto: http://msdn.microsoft.com/en-us/library/87zae4a3(VS.80).aspx. Si utiliza incorrectamente CT2CA (por ejemplo, utilizando el operador de asignación), se encontrará con problemas. La página de documentación vinculada muestra ejemplos de cómo usarla y cómo no usarla.

Más información:

  • El C en CT2CA indica const. Lo uso cuando sea posible, pero algunas conversiones solo son compatibles con la versión sin const (por ejemplo, CW2A).
  • El T en CT2CA indica que está convirtiendo de un LPCTSTR. Por lo tanto, funcionará independientemente de si su código está compilado con el indicador _UNICODE o no. También puede usar CW2A (donde W indica caracteres anchos).
  • El Un en CT2CA indica que está convirtiendo a un (char 8 bits) cadena "ANSI".
  • Finalmente, el segundo parámetro para CT2CA indica la página de códigos a la que está convirtiendo.

para hacer la conversión inversa (de UTF-8 a LPCTSTR), que podría hacer:

CString myString(CA2CT(russianText, CP_UTF8)); 

En este caso, estamos convirtiendo de una cadena "ANSI" en UTF-8 formato, a un LPCTSTR. El LPCTSTR siempre se supone que es UTF-16 (si se define _UNICODE) o la página de códigos del sistema actual (si _UNICODE no está definido).

+0

Intenté lo que dijiste: eliminé la lista de materiales y cambié mi código por el tuyo. Ahora, los personajes se representan como ??????? ?? Algo sigue desaparecido, ¿tal vez? Voy a publicar una edición. – SeargX

+0

Representado como signos de interrogación ¿dónde? Mira el archivo resultante usando un editor hexadecimal. Debería ver algo como [esto] (http://i.imgur.com/RcUsh.png). Y si lo abre en el Bloc de notas, debería ver [this] (http://imgur.com/Yl3OU.png). De lo contrario, es probable que el texto original no esté codificado correctamente. Esperemos que esté utilizando la definición '_UNICODE' y su entrada sea UTF-16. De lo contrario, debe usar las macros para convertir desde la página de códigos en que se encuentre el texto original a la página de códigos que desee. – Nate

+0

Los signos de interrogación están en el archivo resultante, y todos tienen un código hexadecimal (3F, creo). No estoy usando la definición _UNICODE, y no creo que sea una buena idea. Los caracteres rusos que leo proceden de un archivo XML, que abro con tinyXML, que no es compatible con UTF-16, solo con páginas de codificación UTF-8 y Latin 1. Supongo que tengo que usar las macros, aunque no estoy familiarizado con ellas. – SeargX

6

Tendrás que convertir sWorkingLine a UTF-8 y luego escribirlo en el archivo.

WideCharToMultiByte puede convertir cadenas de Unicode a UTF-8 si selecciona la página de códigos CP_UTF8. MultiByteToWideChar puede convertir caracteres ASCII a unicode.

+0

Al usar esa función, ¿se cambiará todo el texto incluido a más de un byte, o solo los caracteres no ascii? – SeargX

+0

@SeargX, solo el no-ascii si usa UTF-8. –

+0

D: Perfecto, gracias. @Everyone ¿Qué tipo de cadena debo poner mis datos convertidos? TCHAR? ¿Cómo determino la longitud de la línea, que es necesaria en la función multibytetowidechar? – SeargX

0

Asegúrese de estar utilizando Unicode (TCHAR is wchar_t). Luego, antes de escribir los datos, conviértelos usando la función WideCharToMultiByte Win32 API.

+0

'wchar_t' NO ES ** UTF-8 **, es ** UCS-2 **. – rhavin