对于UTF8编码的中文,我们如果想要判断一个字符的长度,需要把它的起始码位提取出来并做比较。但是提取码位时,我们需要做正确的转换。

在如上的需求下,看下面的具体实例:

string s  = "我";
s.length() == 3

如果直接取s[0]做检测,如:

s[0] < 0x80

那么其结果必然为真。因为上述比较中,0x80被当作int的字面值,因此s[0]会隐式转换为 int, 而当作有符号数时,这是一个负数,扩展时使用符号位扩展的,所以其转换出来是一个绝对值特别大的负数!其自然小于正数0x80, 故结果变成真。显然这不是符合我们需要的。

因此,我们有两种方法可以解决这个问题:

第一:与上 0xff , 即 ( s[0] & 0xff )

这样虽然第一次是扩展为负数,但是做位运算时,符号位也会参与,故8位以上、前面所有的位都变成0,这样就成了一个正数、且值就是码位;

第二:显式转为unsigned char.

首先,转为unsigned char避免了char到int的扩展,所以不会有补1的情况。接着,signed 到 unsigned, 就是将最高位不再作为符号位了。所以能够得到预期的结果。

以下是测试代码:

int main(int argc, char *argv[])
{
    string s = "我";
    int k = -2000;
    cout << (k & 0xff) << endl;
    cout << boolalpha
         << (s[0] < 0x80) << endl
         << "to unsigned int : " << static_cast<unsigned int>(s[0]) << endl
         << "equals to 'static_cast<unsigned int>(static_cast<int>(s[0]))' : " << static_cast<unsigned int>(static_cast<int>(s[0])) << endl
         << "to unsigned char : " << static_cast<int>(static_cast<unsigned char>(s[0])) << endl // to int for display( else will display as char)
         << "to & : " << (s[0] & 0xff) << endl ;
    return 0;
}

// output
48
true
to unsigned int : 4294967270
equals to 'static_cast<unsigned int>(static_cast<int>(s[0]))' : 4294967270
to unsigned char : 230
to & : 230