先从C语言标准库string.h的strstr函数说起吧,函数原型大概是这样的:const char * strstr ( const char * str1, const char * str2 );这个函数的用途就是判断str2是不是str1的子串,如果是就返回第一次匹配成功的位置(指针),如果不是就返回NULL。
very good,我迫不及待跃跃欲试了:
[C] 纯文本查看 复制代码
void subEstimate(const char* str, const char* sub) {
const char* position = strstr(str, sub);
if (position == NULL) {
printf(""%s" is Not substring of “%s”!\n", sub, str);
}
else {
printf(""%s" is substring of “%s” from “%s”\n", sub, str, position);
}
}
int main() {
char* str = “abc890xyz”;
char* sub = “890”;
subEstimate(str, sub);
return 0;
}
Nice,输出结果符合预期:
“890” is substring of “abc890xyz” from "890xyz"然而,生活在中国的我们,处理中文也是在所难免,我们常用的字符集有GB2312/GBK/GB18030。
GB2312包含常用简体汉字、拉丁字母、希腊字母、日文假名。
GBK在GB2312的基础上补充了不支持的简体字和繁体字,俄语字母等(非国标,等同于微软CP936字码表)。
GB18030涵盖了中日韩/朝鲜和中国少数民族的文字。
根据使用的不同开发环境,可选的字符集可能有所不同,例如我在VS环境下,默认的是GBK。
下面我们来试试中文:
[C] 纯文本查看 复制代码
?
subEstimate(“电子工程世界论坛EEWORLD”, “电子”);
subEstimate(“电子工程世界论坛EEWORLD”, “庸”);
结果:
“电子” is substring of “电子工程世界论坛EEWORLD” from “电子工程世界论坛EEWORLD”
“庸” is substring of “电子工程世界论坛EEWORLD” from "庸こ淌澜缏厶矱EWORLD"第一行工作的很好,而第二行就略显异常。
问题分析:
[C] 纯文本查看 复制代码
?
void printHex(const char* s) {
const unsigned char t = (const unsigned char)s;
while (*t) {
printf("%02X ", *t++);
}
putchar(’\n’);
}
[C] 纯文本查看 复制代码
?
printf(“下面一行是"电子工程世界论坛EEWORLD"的GBK编码:\n”);
printHex(“电子工程世界论坛EEWORLD”);
结果:
下面一行是"电子工程世界论坛EEWORLD"的GBK编码:
B5 E7 D7 D3 B9 A4 B3 CC CA C0 BD E7 C2 DB CC B3 45 45 57 4F 52 4C 44“庸”的GBK编码是D3 B9,确实可以从第四个字节开始匹配,但是从中文字符上讲,这确是风马牛不相及的匹配,根本扯不上关系。
这个问题的根源,就是编码的缺陷,以GB2312字符集为例,GB2312的第一个字节的取值范围是0xA1-0xF7,第二个字节的取值范围是0xA1-0xFE,因为这两个字节的取值范围在很大程度上重叠,所以前一个汉字的后一个字节和后一个汉字的前一个字节很大概率上就是另外一个汉字(GBK和GB18030的取值范围更广,问题面更大),因此在对待汉字的查找问题上,要多加留意。
解决办法呢?
方案1. 使用wchar_t类型以及wchar.h中的函数来操作中文字符串。
[C] 纯文本查看 复制代码
?
#include
const wchar_t * t = wcsstr(L"电子工程世界论坛EEWORLD", L"庸");
方案2. 换用Unicode编码,如UTF-8,UTF-8编码不存在这个问题,UTF-8的第一个字节和后续字节不存在重叠,不会出现从中间匹配的现象。Unicode编码是更通用更安全的编码 ,不过在单片机编程领域,GB2312用的好像更多,所以这个方案可行性小。
方案3. 自己手动撸码进行各种操作也是可行的。
字符串处理一直是编程中最常见的操作,尤其在软件领域,对待字符串要严谨严肃,否则稍不注意就会被人利用漏洞,进行各种攻击。历史上很多软件、操作系统的漏洞就是由字符集问题引发的,比如PHP曾经就出现过一次安全漏洞,在对字符串进行转义的时候未考虑字符编码问题,直接在引号,反斜杠之类的字符前加反斜杠字符,但假如用户输入的字符中包含0xD5,0x27序列,0x27是单引号,在它前面插入0x5C(反斜杠),而D5 5C组成了一个合法的中文字符,这样反斜杠就被吃掉了,后面的内容没有达到转义的效果,就会被用于SQL注入之类的攻击。