UTF-8、CString 和 CFile? (C++,MFC)
Posted
技术标签:
【中文标题】UTF-8、CString 和 CFile? (C++,MFC)【英文标题】:UTF-8, CString and CFile? (C++, MFC) 【发布时间】:2011-01-20 01:57:41 【问题描述】:我目前正在开发一个必须使用 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
我错过了什么吗?我应该改用别的东西吗?有什么我错过的吗? 程序员们,我会为您的智慧和经验而努力。
编辑: 当然,正如我刚刚提出的一个问题,我终于找到了一些可能很有趣的东西,可以在here 找到。想我可以分享一下。
编辑 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。
万岁!谢谢大家!
【问题讨论】:
您需要使用 _tcslen 来获取 TCHAR 字符串的长度。比如:TCHAR* testTCHAR = _T("test"); int tcharLength = _tcslen(testTCHAR); 另外,如果您需要将 CString 转换为 TCHAR* 字符串,请尝试此 CString testCString = _T("test"); TCHAR* testTCHAR = testCString.GetBuffer(); 你工作太辛苦了。将CT2CA
与第二个参数CP_UTF8
一起使用。请参阅下面的帖子。
【参考方案1】:
当你输出数据时你需要做的(这里假设你是在 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
(例如,使用赋值运算符)你会遇到麻烦。链接的文档页面显示了如何使用和不使用它的示例。
更多信息:
CT2CA
中的 C 表示const
。我尽可能使用它,但某些转换仅支持非常量版本(例如CW2A
)。
CT2CA
中的 T 表示您正在从 LPCTSTR
转换。因此,无论您的代码是否使用 _UNICODE
标志编译,它都会起作用。您也可以使用 CW2A
(其中 W 表示宽字符)。
CT2CA
中的 A 表示您正在转换为“ANSI”(8 位字符)字符串。
最后,CT2CA
的第二个参数表示您要转换到的代码页。
要进行反向转换(从 UTF-8 到 LPCTSTR),您可以:
CString myString(CA2CT(russianText, CP_UTF8));
在这种情况下,我们将 从 UTF-8 格式的“ANSI”字符串转换为 LPCTSTR。 LPCTSTR
始终假定为 UTF-16(如果定义了 _UNICODE
)或当前系统代码页(如果未定义 _UNICODE
)。
【讨论】:
我尝试了您所说的:我删除了 BOM,并为您更改了代码。现在,字符表示为??????? ???。还是少了点什么,也许吧?我会发布一个编辑。 用问号表示在哪里?使用十六进制编辑器查看生成的文件。您应该会看到类似this 的内容。如果你在记事本中打开它,你应该会看到this。如果不是,那么您的原始文本可能没有正确编码。希望您使用的是_UNICODE
定义并且您的输入是 UTF-16。如果没有,您需要使用宏将原始文本所在的任何代码页转换为您想要的代码页。
问号在结果文件中,并且都有一个问号十六进制代码(我认为是 3F)。我没有使用 _UNICODE 定义,我认为这不会是好主意。我读取的俄语字符来自一个 XML 文件,我使用 tinyXML 打开该文件,该文件不支持 UTF-16,仅支持 UTF-8 和 Latin 1 编码页面。我想我必须使用宏,虽然我不熟悉它们。
我可以通过关闭 Unicode 模式来复制您的问题。我更新了答案以显示该怎么做。我强烈建议使用 Unicode 模式。使用宏,您可以轻松地将文本转换为tinyXML 的UTF-8 或从UTF-8 转换,即使定义了_UNICODE
。
还有一件事。如果来自 tinyXML 的数据已经是 UTF-8,那么不需要转换,对吧?您确实需要始终准确地知道每个字符串的编码方式。【参考方案2】:
您必须将 sWorkingLine
转换为 UTF-8,然后将其写入文件中。
如果您选择CP_UTF8
代码页,WideCharToMultiByte 可以将 unicode 字符串转换为 UTF-8。
MultiByteToWideChar 可以将 ASCII 字符转换为 unicode。
【讨论】:
使用这样的函数,会不会把所有包含的文本都改成一个字节以上,还是只改成非ascii字符? @SeargX,如果你使用 UTF-8,只有非 ascii。 D:完美,谢谢。 @Everyone 我应该将转换后的数据放入哪种类型的字符串? TCHAR?如何确定 multibytetowidechar 函数中需要的行的长度?【参考方案3】:确保您使用的是 Unicode(TCHAR 是 wchar_t)。然后在写入数据之前,使用 WideCharToMultiByte Win32 API 函数对其进行转换。
【讨论】:
wchar_t
不是 UTF-8,而是 UCS-2。以上是关于UTF-8、CString 和 CFile? (C++,MFC)的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Unicode 中的 CFile::Read() 文件中获取 CString 对象?