以下可能不符合 SO 问题;如果超出范围,请随时告诉我离开。这里的问题基本上是,“我是否正确理解了 C 标准,这是正确的处理方式吗?”
我想就我对 C(以及 C++ 和 C++0x)中字符处理的理解要求澄清、确认和更正。首先,一个重要的观察:
可移植性和序列化是正交的概念。
可移植的东西是像 C, unsigned int
, wchar_t
. 可序列化的东西是uint32_t
UTF-8 之类的东西。“可移植”意味着您可以重新编译相同的源代码并在每个支持的平台上获得工作结果,但二进制表示可能完全不同(甚至不存在,例如 TCP-over-carrier pigeon)。另一方面,可序列化的东西总是具有相同的表示形式,例如我可以在 Windows 桌面、手机或牙刷上读取的 PNG 文件。可移植的东西是内部的,可序列化的东西处理 I/O。可移植的东西是类型安全的,可序列化的东西需要类型双关。</序言>
当谈到 C 中的字符处理时,有两组分别与可移植性和序列化相关:
wchar_t
,setlocale()
,mbsrtowcs()
/wcsrtombs()
:C 标准没有提到“编码”;事实上,它与任何文本或编码属性完全无关。它只说“你的入口点是main(int, char**)
;你得到一个wchar_t
可以保存系统所有字符的类型;你得到读取输入字符序列并将它们变成可用的 wstrings 的函数,反之亦然。iconv()
和 UTF-8,16,32:一个函数/库,用于在定义明确的、明确的、固定的编码之间进行转码。iconv 处理的所有编码都得到普遍理解和认可,但有一个例外。
可移植的、与编码无关的 C 及其wchar_t
可移植字符类型与确定性外部世界之间的桥梁是WCHAR-T 和 UTF 之间的 iconv 转换。
那么,我是否应该始终将我的字符串内部存储在与编码无关的 wstring 中,通过 与 CRT 接口wcsrtombs()
并iconv()
用于序列化?从概念上讲:
my program
<-- wcstombs --- /==============\ --- iconv(UTF8, WCHAR_T) -->
CRT | wchar_t[] | <Disk>
--- mbstowcs --> \==============/ <-- iconv(WCHAR_T, UTF8) ---
|
+-- iconv(WCHAR_T, UCS-4) --+
|
... <--- (adv. Unicode malarkey) ----- libicu ---+
实际上,这意味着我将为我的程序入口点编写两个样板包装器,例如对于 C++:
// Portable wmain()-wrapper
#include <clocale>
#include <cwchar>
#include <string>
#include <vector>
std::vector<std::wstring> parse(int argc, char * argv[]); // use mbsrtowcs etc
int wmain(const std::vector<std::wstring> args); // user starts here
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
extern "C" int main()
{
setlocale(LC_CTYPE, "");
int argc;
wchar_t * const * const argv = CommandLineToArgvW(GetCommandLineW(), &argc);
return wmain(std::vector<std::wstring>(argv, argv + argc));
}
#else
extern "C" int main(int argc, char * argv[])
{
setlocale(LC_CTYPE, "");
return wmain(parse(argc, argv));
}
#endif
// Serialization utilities
#include <iconv.h>
typedef std::basic_string<uint16_t> U16String;
typedef std::basic_string<uint32_t> U32String;
U16String toUTF16(std::wstring s);
U32String toUTF32(std::wstring s);
/* ... */
这是仅使用纯标准 C/C++ 编写惯用的、可移植的、通用的、与编码无关的程序核心的正确方法,以及使用 iconv 的定义良好的 UTF I/O 接口吗?(请注意,Unicode 规范化或变音符号替换等问题超出了范围;只有在您确定您确实需要Unicode(而不是您可能喜欢的任何其他编码系统)之后,才是处理这些细节的时候了,例如使用专用库像libicu。)
更新
在许多非常好的评论之后,我想补充一些意见:
如果您的应用程序明确想要处理 Unicode 文本,您应该将
iconv
-conversion 部分作为核心并在 UCS-4 内部使用uint32_t
/ -strings。char32_t
Windows:虽然使用宽字符串通常很好,但与控制台(任何控制台,就此而言)的交互似乎是有限的,因为似乎不支持任何合理的多字节控制台编码并且
mbstowcs
基本上无用(其他而不是微不足道的扩大)。GetCommandLineW
例如,从 Explorer-drop 和+一起接收宽字符串参数CommandLineToArgvW
(也许应该有一个单独的 Windows 包装器)。文件系统:文件系统似乎没有任何编码概念,只是将任何以空字符结尾的字符串作为文件名。大多数系统采用字节字符串,但 Windows/NTFS 采用 16 位字符串。
char16_t
在发现哪些文件存在以及处理该数据时(例如,不构成有效 UTF16 的序列(例如裸代理)是有效的 NTFS 文件名),您必须小心。标准 Cfopen
无法打开所有 NTFS 文件,因为没有可能的转换将映射到所有可能的 16 位字符串。_wfopen
可能需要使用特定于 Windows 的。作为推论,通常没有明确定义的“多少个字符”概念构成一个给定的文件名,因为首先没有“字符”的概念。买者自负。