我想将一个宽字符串写为文件序列。我尝试了两种方法,第一种方式:
std::wstring str = L"This is a test";
LPBYTE pBuf = (LPBYTE)str.c_str();
FILE* hFile = _wfopen( L"c:\\temp.txt", L"w" );
for( int i = 0; i<(str.length()*sizeof(wchar_t)); ++i)
fwprintf( hFile, L"%02X", pBuf[i] );
fclose(hFile);
第二种方式:
std::wstring str = L"This is a test";
LPBYTE pBuf = (LPBYTE)str.c_str();
HANDLE hFile = CreateFile( L"c:\\temp.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
DWORD dwRet;
WriteFile( hFile, pBuf, str.length()*sizeof(wchar_t), &dwRet, NULL );
CloseHandle(hFile);
当我打开结果文件时,在第一种情况下,文件的内容是: 54006800690073002000690073002000610020007400650073007400
在第二种情况下,文件的内容是: 这是一个测试
为什么第一种方式不能按预期工作?看起来两种方式都是平等的。
答案 0 :(得分:3)
在第一个示例中,您使用fwprintf将字节格式化为2位十六进制字符串,这就是您在该文件中看到十六进制的原因。
我怀疑你应该花一些时间研究ASCII码和UTF-16LE并使用十六进制编辑器查看文本。
每个文件只是一个字节序列,所以你的问题定义不明确,让我觉得你对字节和编码有一些基本的误解,但我不确定它是什么。
答案 1 :(得分:2)
假设您要写出字符串的内存中表示:
#include <fstream>
int main (int argc,char *argv[]) {
std::wstring str = L"This is a test";
std::ofstream fout(R"(c:\temp.txt)");
fout.exceptions(std::ios::badbit | std::ios::failbit);
fout.write(reinterpret_cast<const char*>(str.data()), sizeof(wchar_t) * str.size());
}
我们使用ofstream
,因为这是C ++,最好使用RAII类型,而不必手动调用fclose
或CloseHandle
。我们使用原始字符串作为文件名,因此我们不必处理转义反斜杠。 (在使用合理路径分隔符的平台上;)此处的原始字符串是不必要的。)我们还打开异常,以便我们不必显式检查错误。
然后我们使用write
成员函数写出字节。请注意,codecvt
构面仍应用于使用此方法编写的数据。这就是我们使用ofstream
代替wofstream
的原因; ofstream的默认方面什么都不做,但wofstream
的默认方面会使用默认语言环境将wchar_t转换为char。
如果您只是想写出UTF-16数据,那么有比编写wchar_t
字符串的原始字节更好的方法。 (wchar_t
不一定是UTF-16。有些平台碰巧使用UTF-16。)
一种方法是使用codecvt_utf16
方面:
#include <fstream>
#include <codecvt>
int main(int argc, char *argv[]) {
std::wstring str = L"This is a test";
std::wofstream fout(R"(C:\temp.txt)");
fout.exceptions(std::ios::badbit | std::ios::failbit);
fout.imbue(std::locale(std::locale("C"), new std::codecvt_utf16<wchar_t>));
fout << str;
}
这里我们通常会写一个wchar_t
字符串,但我们已经在wstream中添加了codecvt_utf16
,以便将wchar_t
转换为UTF-16。如果你想要小端UTF-16,或者你想在文件的开头包含U + FEFF(这些经常在Windows上完成),那么有一些标志可以启用:std::codecvt_utf16<wchar_t, 0x10FFFF, std::codecvt_mode::generate_header | std::codecvt_mode::little_endian>
。 (另请注意,codecvt_utf16
会将wchar_t
视为UCS-2或UCS-4,而不是UTF-16。结果是这只会处理Windows上的BMP)
另一个选择是使用普通流和wstring_convert
工具:
#include <fstream>
#include <codecvt>
int main(int argc, char *argv[]) {
std::wstring str = L"This is a test";
std::ofstream fout(R"(C:\temp.txt)");
fout.exceptions(std::ios::badbit | std::ios::failbit);
std::wstring_convert<std::codecvt_utf16<wchar_t>, wchar_t> convert;
fout << convert.to_bytes(str);
}
这可能是我选择的选项,因为它可以让人几乎完全避免wchar_t
。