tl;dr:使用以下任意一种:
-
CStringW value( sqlite3_column_text16() );(可选地将 SQLite 的内部编码设置为 UTF-16),或者
CStringW value( CA2WEX( sqlite3_column_text(), CP_UTF8 ) );
其他的一切都不管用,不管怎样。
第一件事:CStringT 是一个类模板,参数化(除其他外)它用于表示存储序列的字符类型。这是作为BaseType 模板类型参数传递的。有 2 个具体的模板实例化,CStringA 和 CStringW,分别使用 char 和 wchar_t 来存储字符序列1。
CStringT 公开了以下描述模板实例化属性的predefined types:
-
XCHAR:用于存储序列的字符类型。
-
YCHAR: 实例可以相互转换的字符类型。
下表显示了CStringA和CStringW的具体类型:
| XCHAR | YCHAR
---------+---------+--------
CStringA | char | wchar_t
CStringW | wchar_t | char
虽然CStringT 实例化的存储对所使用的字符编码没有任何限制,但转换 c'tors 和运算符是基于以下假设实现的:
-
char 代表 ANSI2 编码的代码单元。
-
whcar_t 代表 UTF-16 编码的代码单元。
如果您的程序不符合这些假设,强烈建议禁用隐式的宽到窄和窄到宽的转换。为此,请在包含任何 ATL/MFC 头文件之前定义 _CSTRING_DISABLE_NARROW_WIDE_CONVERSION 预处理器符号。建议您这样做,即使您的程序满足了防止意外转换的假设,这种转换既昂贵又具有潜在的破坏性。
说完这些,让我们继续提问:
为什么会有两个版本,一个用于const char* (LPCSTR),一个用于unsigned char*?
这很简单:方便。重载只允许您构造一个CString 实例,而不管字符类型3 的符号。采用const unsigned char* 参数的重载实现“转发”到采用const char* 的c'tor:
CSTRING_EXPLICIT CStringT(_In_z_ const unsigned char* pszSrc) :
CThisSimpleString( StringTraits::GetDefaultManager() )
{
*this = reinterpret_cast< const char* >( pszSrc );
}
我应该为不同的情况使用哪个版本?
没关系,只要你构造一个CStringA,即不应用转换。如果您正在构建CStringW,则不应使用其中任何一个(如上所述)。
例如CStringT("Hello")是使用第一版还是第二版?
"Hello" 是const char[6] 类型,当传递给CString c'tor 时,它会衰减为数组中第一个元素的const char*。它使用const char* 参数调用重载。
从第三方获取以空结尾的字符串时,例如sqlite3_column_text() (see here),我应该将其转换为char* 还是unsigned char *?即,我应该使用CString((LPCSTR)sqlite3_column_text(...)) 还是CString(sqlite3_column_text(...))?
SQLite 在这种情况下采用 UTF-8 编码。 CStringA 可以存储 UTF-8 编码的文本,但这样做真的,真的很危险。 CStringA 采用 ANSI 编码,您的代码的读者也可能会这样做。建议将您的 SQLite 数据库更改为存储 UTF-16(并使用sqlite_column_text16)来构造CStringW。如果这不可行,请在使用 CA2WEX 宏将数据存储到 CStringW 实例之前手动从 UTF-8 转换为 UTF-16:
CStringW data( CA2WEX( sqlite3_column_text(), CP_UTF8 ) );
似乎两者都可以,对吗?
这是不正确的。一旦您从数据库中获取非 ASCII 字符,这两种方法都不起作用。
为什么char* 版本会构造一个“Unicode”CStringT 而unsigned char* 版本会构造一个CStringT?
这看起来是文档试图紧凑的结果。 CStringT 是一个类模板。它既不是 Unicode,也不存在。我猜constructors 上的备注部分旨在强调从 ANSI 输入构造 Unicode 字符串的能力(反之亦然)。也简要提到了这一点(“请注意,其中一些构造函数充当转换函数。”)。
总而言之,以下是使用 MFC/ATL 字符串时的通用建议列表:
1还有CString 使用通用文本映射TCHAR 作为其BaseType。 TCHAR 扩展为 char 或 wchar_t,具体取决于预处理器符号。因此,CString 是 CStringA 或 CStringW 的别名,具体取决于这些相同的预处理器符号。 除非您的目标是 Win9x,否则不要使用任何通用文本映射。
2与 Unicode 编码不同,ANSI 不是自包含的表示。代码单元的解释取决于外部状态(当前活动的语言环境)。 除非您与旧代码交互,否则请勿使用。
3由实现定义,char 被解释为有符号还是无符号。无论哪种方式,char、unsigned char 和 signed char 都是 3 种不同的类型。默认情况下,Visual Studio 将 char 解释为已签名。