UTF-8, CString и CFile?(C ++, MFC) - PullRequest
       29

UTF-8, CString и CFile?(C ++, MFC)

15 голосов
/ 23 февраля 2010

В настоящее время я работаю над программой MFC, которая специально работает с UTF-8. В какой-то момент я должен записать данные UTF-8 в файл; Для этого я использую CFiles и CStrings.

Когда я записываю в файл данные utf-8 (точнее, русские символы), вывод выглядит как

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

и т. Д. Это, конечно, не utf-8. Чтобы правильно прочитать эти данные, мне нужно изменить системные настройки; замена не-ASCII-символов на русскую таблицу кодирования работает, но тогда все мои латинские не-ASCII-символы дают сбой. Во всяком случае, так я это делаю.

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

Я что-то упустил? Должен ли я использовать что-то еще вместо этого? Есть ли какой-то улов, который я пропустил? Я буду настроен на вашу мудрость и опыт, коллеги-программисты.

EDIT: Конечно, когда я только что задал вопрос, я наконец-то нашел кое-что интересное, что можно найти здесь . Думаю, я мог бы поделиться этим.

РЕДАКТИРОВАТЬ 2:

Хорошо, поэтому я добавил спецификацию в свой файл, который теперь содержит китайский символ, возможно, потому что я не конвертировал свою строку в UTF-8. Чтобы добавить бомбу я сделал ...

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

И после этого я добавил ...

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

Но тогда я не могу скомпилировать, так как я не знаю, как получить длину TestLine. Стрлен, кажется, не принимает TCHAR. Исправлено, вместо этого использовалась статическая длина 1000.

РЕДАКТИРОВАТЬ 3:

Итак, я добавил этот код ...

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

Он прекрасно компилируется, но когда я смотрю на мой новый файл, он точно такой же, как и когда у меня не было всего этого нового кода (например: я не знаю :)). Такое ощущение, что я не сделал ни шагу вперед, хотя, полагаю, только мелочь отделяет меня от победы.

РЕДАКТИРОВАТЬ 4:

Я удалил ранее добавленный код, как и спросил Нейт, и решил вместо этого использовать его код, то есть теперь, когда я добавляю свою строку, у меня есть ...

        CT2CA outputString(sWorkingLine, CP_UTF8);

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

Все компилируется нормально, но русские символы отображаются как ??????? Все ближе, но все же не то. Кстати, я хотел бы поблагодарить всех, кто пытался / пытается помочь мне, это очень ценится. Я застрял на этом некоторое время, я не могу дождаться, когда эта проблема исчезнет.

ЗАКЛЮЧИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ (надеюсь) Изменив способ, которым я впервые получил свои символы UTF-8 (я перекодировал без особого знания), что было ошибочным с моим новым способом вывода текста, я получил приемлемые результаты. Добавив символ UTF-8 BOM в начало моего файла, он может быть прочитан как Unicode в других программах, таких как Excel.

Ура! Спасибо всем!

Ответы [ 3 ]

25 голосов
/ 23 февраля 2010

Когда вы выводите данные, которые вам нужно сделать (это предполагает, что вы компилируете в режиме Unicode, что настоятельно рекомендуется):

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

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

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

Если _UNICODE не определено (вместо этого вы работаете в многобайтовом режиме), вам нужно знать, в какой кодовой странице находится ваш вводимый текст, и преобразовать его в то, что вы можете использовать. В этом примере показана работа с русским текстом в формате UTF-16 с сохранением его в формате 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));

Скорее всего, ваш русский текст находится в другой кодовой странице, такой как KOI-8R. В этом случае вам нужно конвертировать из другой кодовой страницы в UTF-16. Затем конвертируйте UTF-16 в UTF-8. Вы не можете преобразовать напрямую из KOI-8R в UTF-8, используя макросы преобразования, потому что они всегда пытаются преобразовать узкий текст в системную кодовую страницу. Таким образом, простой способ сделать это:

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

Вам не нужна спецификация (она необязательна; я бы ее не использовал, если бы для этого не было особой причины).

Обязательно прочитайте это : http://msdn.microsoft.com/en-us/library/87zae4a3(VS.80).aspx. Если вы неправильно введете CT2CA (например, с помощью оператора присваивания), у вас возникнут проблемы. На странице связанной документации приведены примеры того, как использовать и как ее не использовать.

Дополнительная информация:

  • C в CT2CA указывает const. Я использую его, когда это возможно, но некоторые преобразования поддерживают только неконстантную версию (например, CW2A).
  • T в CT2CA указывает, что вы конвертируете из в LPCTSTR. Таким образом, он будет работать независимо от того, скомпилирован ли ваш код с флагом _UNICODE или нет. Вы также можете использовать CW2A (где W обозначает широкие символы).
  • A в CT2CA указывает, что вы конвертируете в строку "ANSI" (8-битный символ).
  • Наконец, второй параметр CT2CA указывает кодовую страницу, в которую вы конвертируете.

Чтобы сделать обратное преобразование (из UTF-8 в LPCTSTR), вы можете сделать:

CString myString(CA2CT(russianText, CP_UTF8));

В этом случае мы конвертируем из строки "ANSI" в формате UTF-8 в LPCTSTR. LPCTSTR всегда считается UTF-16 (если определено _UNICODE) или текущей системной кодовой страницей (если _UNICODE не определено).

6 голосов
/ 23 февраля 2010

Вам нужно будет преобразовать sWorkingLine в UTF-8, а затем записать его в файл.

WideCharToMultiByte может преобразовывать строки Unicode в UTF-8, если вы выберете кодовую страницу CP_UTF8. MultiByteToWideChar может преобразовывать символы ASCII в Unicode.

0 голосов
/ 23 февраля 2010

Убедитесь, что вы используете Unicode (TCHAR - это wchar_t). Затем перед записью данных преобразуйте их с помощью функции Win32 API WideCharToMultiByte.

...