将字符串写入文件作为字节序列

时间:2014-03-01 17:30:43

标签: c++ string winapi std

我想将一个宽字符串写为文件序列。我尝试了两种方法,第一种方式:

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

在第二种情况下,文件的内容是: 这是一个测试

为什么第一种方式不能按预期工作?看起来两种方式都是平等的。

2 个答案:

答案 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类型,而不必手动调用fcloseCloseHandle。我们使用原始字符串作为文件名,因此我们不必处理转义反斜杠。 (在使用合理路径分隔符的平台上;)此处的原始字符串是不必要的。)我们还打开异常,以便我们不必显式检查错误。

然后我们使用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