在构造CString时使用带符号或无符号字符?

时间:2018-11-01 23:07:20

标签: c++ string mfc

我正在检查document for CString。在以下语句中:

  
      
  • CStringT( LPCSTR lpsz ):从ANSI字符串构造Unicode CStringT。您还可以使用此构造函数来加载字符串资源,如下例所示。

  •   
  • CStringT( LPCWSTR lpsz ):根据Unicode字符串构造CStringT

  •   
  • CStringT( const unsigned char* psz ):允许您从指向CStringT的指针构造一个unsigned char

  •   

我有一些问题:

  1. 为什么有两个版本,一个用于const char*LPCSTR),一个用于unsigned char*?在不同情况下应该使用哪个版本?例如,CStringT("Hello")是否使用第一或第二版本?从第三方获取以{* 1}(see here)之类的以null结尾的字符串时,我应该将其转换为sqlite3_column_text()还是char*?即我应该使用unsigned char *还是CString((LPCSTR)sqlite3_column_text(...))?看来两者都可以,对吗?

  2. 为什么CString(sqlite3_column_text(...))版本会构造一个“ Unicode” char*,而CStringT版本会构造一个unsigned char*CStringT是一个模板化类,用于指示所有3个实例,即CStringTCStringCStringA,因此在构造时为什么要强调“ Unicode” CStringW使用CStringTLPCSTR)?

2 个答案:

答案 0 :(得分:3)

LPCSTR只是const char*,而不是const signed char*。根据编译器的实现,char是带符号的还是无符号的,但是出于重载的目的,charsigned charunsigned 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种具体的模板实例化CStringACStringW,它们分别使用charwchar_t存储字符序列 1 。 / p>

CStringT公开了以下描述模板实例化属性的predefined types

  • XCHAR:用于存储序列的字符类型。
  • YCHAR:实例可以与之转换的字符类型。

下表显示了CStringACStringW的具体类型:

         | 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作为其BaseTypeTCHAR扩展为charwchar_t,具体取决于预处理器符号。因此CStringCStringACStringW的别名,这取决于那些非常相同的预处理器符号。 除非您以Win9x为目标,否则请不要使用任何通用文本映射。

2 与Unicode编码不同,ANSI不是独立的表示形式。代码单元的解释取决于外部状态(当前活动的语言环境)。 除非使用旧代码,否则请不要使用。

3 这是实现定义的,是将char解释为带符号的还是无符号的。 charunsigned charsigned char都是3种不同的类型。默认情况下,Visual Studio将char解释为已签名。