[原创] C语言char字符串与中文编码的坑

lcofjp   2017-9-28 07:57 楼主
先从C语言标准库string.h的strstr函数说起吧,函数原型大概是这样的:const char * strstr ( const char * str1, const char * str2 );这个函数的用途就是判断str2是不是str1的子串,如果是就返回第一次匹配成功的位置(指针),如果不是就返回NULL。
very good,我迫不及待跃跃欲试了:
  1. void subEstimate(const char* str, const char* sub) {
  2.         const char* position = strstr(str, sub);
  3.         if (position == NULL) {
  4.                 printf(""%s" is Not substring of "%s"!\n", sub, str);
  5.         }
  6.         else {
  7.                 printf(""%s" is substring of "%s" from "%s"\n", sub, str, position);
  8.         }
  9. }

  10. int main() {
  11.         char* str = "abc890xyz";
  12.         char* sub = "890";
  13.         subEstimate(str, sub);
  14.         return 0;
  15. }

Nice,输出结果符合预期:
引用: "890" is substring of "abc890xyz" from "890xyz"
然而,生活在中国的我们,处理中文也是在所难免,我们常用的字符集有GB2312/GBK/GB18030。
GB2312包含常用简体汉字、拉丁字母、希腊字母、日文假名。
GBK在GB2312的基础上补充了不支持的简体字和繁体字,俄语字母等(非国标,等同于微软CP936字码表)。
GB18030涵盖了中日韩/朝鲜和中国少数民族的文字。
根据使用的不同开发环境,可选的字符集可能有所不同,例如我在VS环境下,默认的是GBK。
下面我们来试试中文:
  1.         subEstimate("电子工程世界论坛EEWORLD", "电子");
  2.         subEstimate("电子工程世界论坛EEWORLD", "庸");

结果:
引用: "电子" is substring of "电子工程世界论坛EEWORLD" from "电子工程世界论坛EEWORLD"
"庸" is substring of "电子工程世界论坛EEWORLD" from "庸こ淌澜缏厶矱EWORLD"
第一行工作的很好,而第二行就略显异常。
问题分析:
  1. void printHex(const char* s) {
  2.         const unsigned char *t = (const unsigned char*)s;
  3.         while (*t) {
  4.                 printf("%02X ", *t++);
  5.         }
  6.         putchar('\n');
  7. }
  1.         printf("下面一行是"电子工程世界论坛EEWORLD"的GBK编码:\n");
  2.         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中的函数来操作中文字符串。
  1. #include <wchar.h>
  2. 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注入之类的攻击。
当然,在我大单片机上编程很少遇到字符串处理上的问题,但稍微了解一下也不是坏事。

此内容由EEWORLD论坛网友lcofjp原创,如需转载或用于商业用途需征得作者同意并注明出处


回复评论 (21)

字符串处理用的挺多的啊,我毕设里面做汉子库的时候也遇到类似的问题。很不错
亚里士缺德
点赞  2017-9-28 09:00
catpain这是用绳命在科普啊。
LCD字库基本没有除了gb2312之外的东西,串口调试助手们也基本没有除了gb2312之外的编码,板界里除了信息输出基本没有要用中文的地方,才不得不整这编码。
默认摸鱼,再摸鱼。2022、9、28
点赞  2017-9-28 09:05
引用: 换用Unicode编码,如UTF-8,UTF-8编码不存在这个问题,UTF-8的第一个字节和后续字节不存在重叠,不会出现从中间匹配的现象。Unicode编码是更通用更安全的编码
这个叙述不确切。Unicode是字符集,而UTF-8是Unicode字符集的一种编码实现(方式)。另外还有UCS-2, UTF-16也是可能遇到的编码实现,比如Windows NT内部是用UTF-16编码。
点赞  2017-9-28 09:38
对单片机来说,用GB3212编码是最省内存(RAM/ROM)的,用UCS-2对128以内的ASCII会浪费空间,用UTF-8则每个汉字要多占一个字节的存储。字库的编排检索上GB2312也是最容易的。
GB2312还有一个好处是用等宽字体的时候,strlen() 就能确定一行字符串(不包含控制字符)显示需要的宽度,因为一个汉字编码两个字节,刚好占两个ASCII字符宽度。DOS时代这是标配。
点赞  2017-9-28 09:51
引用: freebsder 发表于 2017-9-28 09:05
catpain这是用绳命在科普啊。
LCD字库基本没有除了gb2312之外的东西,串口调试助手们也基本没有除了gb2312 ...

人机交互界面用的多啊。LCD字库怎么不能用UTF字符集呢。
亚里士缺德
点赞  2017-9-28 09:51
引用: 汤权 发表于 2017-9-28 09:51
人机交互界面用的多啊。LCD字库怎么不能用UTF字符集呢。

字库不是网站,不用考虑多样性,所有人都用gbk,你非要utf那你爱用不用。从传统到现在gbk/gb2312都是默认编码,编辑器能支持多编码也就这10多年的事,以前你要不是gbk/gb2312根本就是乱码。
一旦gbk/gb2312趋势形成了,所有人都用,而换编码并不能带来实际的好处,utf也就没什么市场了。
默认摸鱼,再摸鱼。2022、9、28
点赞  2017-9-28 10:15
引用: freebsder 发表于 2017-9-28 10:15
字库不是网站,不用考虑多样性,所有人都用gbk,你非要utf那你爱用不用。从传统到现在gbk/gb231 ...

带着火气说话了啊,想必你没做过网页开发,好吧,你觉得gb2312是最好的最全的编码,我也没办法啦。拜拜
亚里士缺德
点赞  2017-9-28 10:22
引用: 汤权 发表于 2017-9-28 10:22
带着火气说话了啊,想必你没做过网页开发,好吧,你觉得gb2312是最好的最全的编码,我也没办法啦。拜拜{: ...

呵呵,我不和你争。你这点道行评价我还差了点。
默认摸鱼,再摸鱼。2022、9、28
点赞  2017-9-28 10:28
引用: cruelfox 发表于 2017-9-28 09:38
这个叙述不确切。Unicode是字符集,而UTF-8是Unicode字符集的一种编码实现(方式)。另外还有UCS-2, UTF-16也 ...

对,你说的对,unicode后面不应该出现编码二字,它是字符集
点赞  2017-9-28 10:40
引用: freebsder 发表于 2017-9-28 09:05
catpain这是用绳命在科普啊。
LCD字库基本没有除了gb2312之外的东西,串口调试助手们也基本没有除了gb2312 ...

我要写串口助手肯定要支持各种编码啊……
点赞  2017-9-28 10:50
关于 GBK VS UNICODE,我觉得除非做一个卖编全世界的东西,或者做个网站什么的,否则确实不需要太纠结.....
我比较纠结的是字体丑死
强者为尊,弱者,死无葬身之地
点赞  2017-9-28 11:00
引用: 辛昕 发表于 2017-9-28 11:00
关于 GBK VS UNICODE,我觉得除非做一个卖编全世界的东西,或者做个网站什么的,否则确实不需要太纠结..... ...

字体基本不用国内的,我用MingLiu比较多
点赞  2017-9-28 12:51
引用: yubinwu 发表于 2017-9-28 12:51
字体基本不用国内的,我用MingLiu比较多

那是啥?
现在不是有 思源 么
天哪,我忽然想起我欠了琳姐一笔很大的债@soso
强者为尊,弱者,死无葬身之地
点赞  2017-9-28 14:05
引用: 辛昕 发表于 2017-9-28 14:05
那是啥?
现在不是有 思源 么
天哪,我忽然想起我欠了琳姐一笔很大的债@soso

欠债你得还呀 亲 
加油!在电子行业默默贡献自己的力量!:)
点赞  2017-9-29 10:09
队长,你可以开个博客,或者公众号讲课了,绝逼的高质量,吸粉吸offer都不是问题
HELLO_WATER
点赞  2017-9-29 10:11
引用: shinykongcn 发表于 2017-9-29 10:11
队长,你可以开个博客,或者公众号讲课了,绝逼的高质量,吸粉吸offer都不是问题

不敢当啊,都是些小儿科的内容
点赞  2017-9-29 11:37
这事我记得以前我是这么玩的,把eeworld 或者数字神马的,一样用GBK编码就好,就是和看起来样子比较怪,就是输入法全角下输入的那个死样子。
点赞  2017-10-2 15:45
引用: soso 发表于 2017-9-29 10:09
欠债你得还呀 亲 

哦,好~~
强者为尊,弱者,死无葬身之地
点赞  2017-10-2 15:46
尽管用不到,了解下没坏处
点赞  2017-10-10 09:54
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复