我正在检查document for CString。在以下语句中:
CStringT( LPCSTR lpsz )
:从ANSI字符串构造UnicodeCStringT
。您还可以使用此构造函数来加载字符串资源,如下例所示。
CStringT( LPCWSTR lpsz )
:根据Unicode字符串构造CStringT
。
CStringT( const unsigned char* psz )
:允许您从指向CStringT
的指针构造一个unsigned char
。
我有一些问题:
为什么有两个版本,一个用于const char*
(LPCSTR
),一个用于unsigned char*
?在不同情况下应该使用哪个版本?例如,CStringT("Hello")
是否使用第一或第二版本?从第三方获取以{* 1}(see here)之类的以null结尾的字符串时,我应该将其转换为sqlite3_column_text()
还是char*
?即我应该使用unsigned char *
还是CString((LPCSTR)sqlite3_column_text(...))
?看来两者都可以,对吗?
为什么CString(sqlite3_column_text(...))
版本会构造一个“ Unicode” char*
,而CStringT
版本会构造一个unsigned char*
? CStringT
是一个模板化类,用于指示所有3个实例,即CStringT
,CString
,CStringA
,因此在构造时为什么要强调“ Unicode” CStringW
使用CStringT
(LPCSTR
)?
答案 0 :(得分:3)
LPCSTR
只是const char*
,而不是const signed char*
。根据编译器的实现,char
是带符号的还是无符号的,但是出于重载的目的,char
,signed char
和unsigned char
是3种不同的类型。 C ++中的字符串文字类型为const char[]
,因此CStringT("Hello")
将始终使用LPCSTR
构造函数,而不是unsigned char*
构造函数。
sqlite3_column_text(...)
返回unsigned char*
,因为它返回UTF-8编码的文本。我不知道unsigned char*
的{{1}}构造函数实际上是做什么的(它与MBCS字符串有关),但是CStringT
构造函数使用用户的[*]执行从ANSI到UNICODE的转换。默认语言环境。这将破坏包含非ASCII字符的UTF-8文本。
在这种情况下,您最好的选择是将UTF-8文本转换为UTF-16(使用LPCSTR
或等效语言,或者简单地使用MultiByteToWideChar()
来返回UTF-16编码的文本),然后使用sqlite3_column_text16()
的{{1}}(LPCWSTR
)构造函数,因为Windows对UTF-16数据使用const wchar_t*
。
答案 1 :(得分:2)
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 。 / p>
CStringT
公开了以下描述模板实例化属性的predefined types:
XCHAR
:用于存储序列的字符类型。YCHAR
:实例可以与之转换的字符类型。下表显示了CStringA
和CStringW
的具体类型:
| XCHAR | YCHAR
---------+---------+--------
CStringA | char | wchar_t
CStringW | wchar_t | char
虽然CStringT
实例的存储对于所使用的字符编码没有任何限制,但是转换系数和运算符是基于以下假设实现的:
char
代表ANSI 2 编码的代码单元。whcar_t
代表UTF-16编码的代码单元。如果您的程序与这些假设不符,强烈建议您禁用隐式的宽到窄和窄到宽转换。为此,请在包含任何ATL / MFC头文件之前定义_CSTRING_DISABLE_NARROW_WIDE_CONVERSION
预处理程序符号。即使您的程序符合防止意外转换的假设,建议这样做也是如此,因为转换既昂贵又具有破坏性。
顺便说一句,让我们继续讨论以下问题:
为什么有两个版本,一个用于
const char*
(LPCSTR
),一个用于unsigned char*
?
这很简单:方便。重载仅允许您构造一个CString
实例,而不考虑字符类型 3 的签名。重载的实现是将const unsigned char*
参数“ forwards”带给c'tor采取const char*
:
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]
,当传递给const char*
c'tor时,它会衰减为数组中第一个元素的CString
。它使用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字符串时的一般建议列表:
CStringW
。这是唯一的隐含字符编码是明确的(UTF-16)的字符串类型。CStringA
。确保明确记下所使用的字符编码。另外,请确保了解“当前活动的语言环境” 可以随时更改。有关更多信息,请参见Keep your eye on the code page: Is this string CP_ACP or UTF-8?。CString
。仅通过查看代码,就不再清楚这是什么类型(可以是2种类型中的任何一种)。同样,在查看构造函数调用时,将不再可能看到这是复制操作还是转换操作。CStringT
类模板实例化的隐式转换。 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
解释为已签名。