17

我目前正在开发一个专门使用 UTF-8 的 MFC 程序。在某些时候,我必须将 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

我错过了什么吗?我应该改用别的东西吗?有什么我错过的吗?程序员们,我会为您的智慧和经验而努力。

编辑:当然,正如我刚刚提出的一个问题,我终于找到了一些可能很有趣的东西,可以在这里找到。我想我可能会分享它。

编辑2:

好的,所以我将 BOM 添加到我的文件中,该文件现在包含中文字符,可能是因为我没有将我的行转换为 UTF-8。添加我做的bom...

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 的长度。strlen 似乎不接受 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:

正如 Nate 所要求的,我删除了之前添加的代码,我决定改用他的代码,这意味着现在,当我要添加我的行时,我已经......

        CT2CA outputString(sWorkingLine, CP_UTF8);

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

一切都编译得很好,但是俄语字符显示为???????。越来越近,但仍然不是那样。顺便说一句,我要感谢所有试图/试图帮助我的人,非常感谢。我已经坚持了一段时间了,我等不及这个问题消失了。

最终编辑(我希望)通过改变我第一次获得 UTF-8 字符的方式(我在不知道的情况下重新编码),这与我输出文本的新方式是错误的,我得到了可以接受的结果。通过在我的文件开头添加 UTF-8 BOM 字符,它可以在其他程序(如 Excel)中读取为 Unicode。

欢呼!谢谢大家!

4

3 回答 3

28

当你输出数据时你需要做的(这里假设你在 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));

您不需要 BOM(它是可选的;除非有特定原因,否则我不会使用它)。

确保您阅读此内容:http : //msdn.microsoft.com/en-us/library/87zae4a3 (VS.80).aspx 。如果你错误地使用CT2CA(例如,使用赋值运算符)你会遇到麻烦。链接的文档页面显示了如何使用和不使用它的示例。

更多的信息:

  • 中的CCT2CA表示const。我尽可能使用它,但有些转换只支持非常量版本(例如CW2A)。
  • T inCT2CA表示您正在从. LPCTSTR因此,无论您的代码是否使用_UNICODE标志编译,它都会起作用。您也可以使用CW2A(其中W表示宽字符)。
  • A in表示您正在转换为“ANSI”(8 位字符)字符串CT2CA
  • 最后,第二个参数 toCT2CA指示您要转换到的代码页。

要进行反向转换(从 UTF-8 到 LPCTSTR),您可以执行以下操作:

CString myString(CA2CT(russianText, CP_UTF8));

在这种情况下,我们将UTF-8 格式“ANSI”字符串转换为 LPCTSTR。LPCTSTR始终假定为 UTF-16(如果已_UNICODE定义)或当前系统代码页(如果_UNICODE未定义)。

于 2010-02-23T16:58:03.757 回答
6

您必须转换sWorkingLine为 UTF-8,然后将其写入文件中。

如果选择代码页, WideCharToMultiByteCP_UTF8可以将 unicode 字符串转换为 UTF-8 。 MultiByteToWideChar可以将 ASCII 字符转换为 unicode。

于 2010-02-23T14:01:00.897 回答
0

确保您使用的是 Unicode(TCHAR 是 wchar_t)。然后在写入数据之前,使用 WideCharToMultiByte Win32 API 函数对其进行转换。

于 2010-02-23T14:01:14.360 回答