【问题标题】:Why there is a blank between each char I wrote to a file with WriteFile?为什么我用 WriteFile 写入文件的每个字符之间都有一个空格?
【发布时间】:2015-10-23 19:39:38
【问题描述】:

这是我的代码:

WCHAR msg[] = L"ReplaceFile:";
::WriteFile( hFile, msg, lstrlenW(msg) * sizeof(WCHAR), &nBytes, NULL );  

我使用 OPEN_ALWAYS 模式创建了这个文件,我将向这个文件写入一些 const 字符串。该文件显示“ReplaceFile”,如下所示: R e p l a c e F i l e.

谁能告诉我如何使它正常,为什么? 提前致谢。

【问题讨论】:

  • 宽字符,例如带有 L 前缀的字符,每个字符占用 2 个字节(在 Windows 上)。您需要将字符串更改为适用于字节级别的不同编码,例如您的语言环境的 Windows 默认代码页。
  • 更好的选择是 UTF-8。远离使用语言环境存储字符串。
  • 请定义“正常”。 UTF16 对我来说很正常。
  • @MarkRansom:在 UTF-16 中,宽字符每个字符(代码点)占用 2 或 4 个字节。您将 codepointcodeunit 混淆了。使用 UTF-16 编码单元占用 2 个字节。
  • @RemyLebeau 除非您知道将如何使用该文件,否则您不能做出这样的笼统声明。如果您将它提供给另一个 Windows 应用程序,那么 UTF-8 很有可能无法正常工作。我也希望全世界都对 UTF-8 进行标准化,但有时你别无选择,只能捏着鼻子。

标签: c++ windows winapi unicode


【解决方案1】:

WCHARwchar_t 的别名,在 Windows 上为 2 个字节。 Windows 上的宽字符串以 UTF-16LE 编码。在 UTF-16 中,每个元素(称为代码单元)的大小为 2 字节(16 位),其中 Unicode 代码点 U-0000 - U-FFFF 占用一个代码单元,更高的代码点占用两个代码单元。

您的宽字符串仅包含 ASCII 字符,这些字符小于 0x0080,因此它们每个使用不超过 7 位,至少有 9 位设置为 0。因此,每隔一个字节写入该文件的值为 0x00,这不是可显示的字符,因此您看到的额外间距。

您的宽字符串 L"ReplaceFile:" 由以下 UTF-16LE 字节组成:

0x52 0x00 // R
0x65 0x00 // e
0x70 0x00 // p
0x6C 0x00 // l
0x61 0x00 // a
0x63 0x00 // c
0x65 0x00 // e
0x46 0x00 // F
0x69 0x00 // i
0x6C 0x00 // l
0x65 0x00 // e
0x3A 0x00 // :

您应该阅读以下文章:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

话虽如此,UTF-16 并不是在文件中存储字符串的最佳选择。对于大多数语言来说,UTF-8 比 UTF-16 更简洁,并且向后兼容 ASCII。在 Windows 上,您可以使用 WideCharToMultiByte() 函数(或类似的函数/库)在将宽字符串写入文件之前对其进行转换:

WCHAR msg[] = L"ReplaceFile:";
int len = WideCharToMultiByte(CP_UTF8, 0, msg, lstrlenW(msg), NULL, 0, NULL, NULL);
CHAR *converted = new CHAR[len];
WideCharToMultiByte(CP_UTF8, 0, msg, lstrlenW(msg), converted, len, NULL, NULL);
::WriteFile( hFile, converted, len * sizeof(CHAR), &nBytes, NULL );  
delete [] converted;

【讨论】:

  • “谁能告诉我如何使它正常,为什么?”。您似乎回答了为什么,但没有回答如何使其正常
  • 没有任何异常。这是一个完全正常和标准的编码。他应该问的是“如何以一种没有额外空值/空格的方式对其进行编码”。答案是使用WideCharToMultiByte() 或类似函数将宽字符串从UTF-16 转换为更简洁的编码,如UTF-8。
  • @RemyLebeau 或将原始字符串定义为 const char * 而不是 WCHAR 哈哈
  • char msg[] = "ReplaceFile:"; 会省一大堆垃圾
  • 当然,在文件开头添加 BOM 将是一种替代解决方案,因为它有助于说服任何体面的文本编辑器将数据正确解释为 UTF-16 并显示用户最有可能的内容预计。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-28
  • 1970-01-01
  • 2016-06-10
相关资源
最近更新 更多