我在使用下面的sqlite3语句进行更新数据库的cpp代码。在处理中文时出现错误
const char *sql = "UPDATE share_map SET target_path = ? || SUBSTR(target_path, ?) WHERE target_path LIKE ? OR target_path = ?;";
...
int old_path_length = pathstr.length();
sqlite3_bind_int(stmt, 2, old_path_length+1);//从length+1截取
原因是使用的sqlite3的SUBSTR函数是根据字符位置截取的。而std::string length()函数返回的是字节数。对于UTF8编码的字符,每个字符可能是1个,也可能是多个字节,这就造成了不统一,错误。
比如"abc中文"这个字符串,utf8对于汉字是3字节1字符,英文字母是1字节1字符,所以length()函数会返回9,实际上期望的是5。
解决办法是
A. 把字符串绑定到sqlite3语句,调用sqlite3自己的函数来计算字符数。
B. 自己手动计算utf8字符数
B办法我使用
代码是
size_t utf8_strlen(const std::string& str) {
size_t len = 0;
for (unsigned char c : str) {
if ((c & 0xC0) != 0x80) { // 统计UTF-8字符起始字节
++len;
}
}
return len;
}
这个函数通过检测UTF-8编码的字节特征来统计字符数,原理是续字节识别。
UTF-8编码规则
UTF-8使用1-4个字节表示一个字符,编码规则如下:
| 字符类型 | 字节1范围 | 字节2范围 | 字节3范围 | 字节4范围 | 总字节数 |
|---|---|---|---|---|---|
| 单字节字符 | 0x00-0x7F | - | - | - | 1 |
| 双字节字符 | 0xC0-0xDF | 0x80-0xBF | - | - | 2 |
| 三字节字符 | 0xE0-0xEF | 0x80-0xBF | 0x80-0xBF | - | 3 |
| 四字节字符 | 0xF0-0xF7 | 0x80-0xBF | 0x80-0xBF | 0x80-0xBF | 4 |
关键原理:
在UTF-8中:
- 起始字节:最高位是
11(多字节字符的开始)或0(单字节字符) - 续字节:最高位是
10(表示这是多字节字符的后续部分)
判断条件分解:
0xC0二进制:110000000x80二进制:10000000(c & 0xC0):取字节的最高2位(c & 0xC0) != 0x80:如果最高2位不是10,说明这是一个字符的起始字节
所以将utf8_strlen替换length函数就行了