作者:非妃是公主
专栏:《笔记》《C++》
博客地址:https://blog.csdn.net/myf_666
个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩

文章目录

  • 一、C++遍历英文字符串
  • 二、C++遍历中文字符串(不会出问题情况)
  • 三、C++遍历中文字符串(会出现问题的情况)
  • 四、英文字符的表示
  • 五、中文字符的表示
    • 5.1 定长编码
    • 5.2 变长编码
    • 5.3 Unicode编码
  • 六、正确的中文字符串遍历方式
  • 六、参考资料
  • the end……

在Java、python等语言中,字符统一采用utf-8进行编码,在处理字符串时,十分方便,不需要关心字符编码的长度。(在编写程序时,每个字符的长度都可理解为1,然后通过get等函数获取第几个字符就可以很好地使用了)

但是,在C++中则不是这样的,C++中的字符编码采用了gbk格式,而且C++的设计原则是,不为了程序编写者的方便而浪费性能,所以在C++中我们通过下标访问字符串的位数则是第几个字节(字节偏移量),表示的并不是字符。

下面是关于C++编码的详细介绍!


一、C++遍历英文字符串

C++遍历英文字符串很简单,基本有两种方法

// 方法1
for(int i=0; i < str.size(); i++){cout << str[i];
}// 方法2
for(auto it = str.begin(); it != str.end(); it++){cout << *it;
}

以上两种方法基本就可以很好地遍历英文字符串了!


二、C++遍历中文字符串(不会出问题情况)

但是中文字符串呢?我们来试一下:

发现是可以正常输出的!


三、C++遍历中文字符串(会出现问题的情况)

再继续试!这次这样尝试,因为我们遍历字符串一般都是要对字符串中的字符进行操作,如果单纯只是输出或者显示而已,没必要去遍历字符,直接cout<<str;就可以了,所以接下来试一下字符的操作:

这次添加了一个if (str[i] == '非') continue;的判断,但是‘非’依然输出了出来
调试一下:

i = 0,但是str[i] == '非'确实false,这是怎么回事呢?
我们知道,遍历字符串主要就是要对字符串进行操作,可是如今字符串判断相等却出现了问题……
这里给出测试代码,感兴趣的老哥可以自己去调试:

void test1() {string str = "非妃是公主";cout << "这时test1:";for (int i = 0; i < str.size(); i++) {       if (str[i] == '非') continue;cout << str[i];}cout << endl;
}void test2() {string str = "非妃是公主";cout << "这时test2:";for (auto it = str.begin(); it != str.end(); it++) {cout << *it;}cout << endl;
}int main() {test1();test2();
}

四、英文字符的表示

其实,这里涉及到了一个编码的问题,ASCII码值是一个字符集表,里面编码了26个英文字母的大小写(大写字母65~90,小写字母97~120),还有其它英文字符(比如空格、单引号……),其中有一些甚至是不可显示的(比如换行符、分组符……)。具体可以查看ASCII码表.

利用ASCII(美国信息交换标准代码)就可以实现英文的字符映射了,因为英文字母只有那么些,所有的单词都是根据这些字母进行排列组合形成的。

下面为ASCII码表的节选(开头和结尾部分):


可以看到,从0~127,ASCII码表有128个编码,而28=2562^8=25628=256,也就是说可以用1个字节(Byte,等于8个bit)大小的内存空间来编码所有的英文字符,因此char利用这样的字节编码到字符进行映射就可以实现英文字符串的运算。


五、中文字符的表示

从上面不难看出,英文可以利用一个很小的字符集(ASCII——美国信息交换标准代码)去表示所有单词(因为只有26个字母等优先的符号),但中文不可以,中华汉字博大精深,其中包含了几千甚至上万的汉字(如果还包括一些繁体字、生僻字等数量会更大)!

因此,1个Byte不能满足中文编码的需要,我们需要2个、3个甚至4个Byte进行编码才能把中文表示出来!

这里就包含了两种编码的方式,定长编码和变长编码。


5.1 定长编码

定长编码:顾名思义,就是每个字符对应的编码的长度都是相等的,这里不得不提到GB2312编码和GBK编码。

  • GB2312编码:就是把汉字编码成两个字节,一个字节有28=2562^8=25628=256种不同的编码,两个字节就有216=655362^{16}=65536216=65536种不同的编码,也就是说我们最多可以编码65536种情况,这些对于常用的文字应该可以了吧……但是,值得一提的是,GB2312并没有使用完全这些编码,它只用了一部分,那么剩下的呢?GB2312为了保持向下兼容ASCII,它避免了和ASCII进行冲突编码,这要浪费一部分编码空间,但依然还是有空余的,这些空余下的位置暂且留着,GB2312没有使用!
  • GBK编码:和 GB2312 一样,GBK 也是双字节编码,同样为了向下兼容 GB2312, GBK使用了GB2312 没有用到的那些编码区域,简单地说,就是进一步拓展了编码集,GBK比GB2312编码了更多的汉字。

可以说,GBK编码是对GB2312编码的补充!

关于定长编码的详细规则可以在这篇文章里看到,总结的十分全面https://zhuanlan.zhihu.com/p/453675608


5.2 变长编码

变长编码:是一种包含多个长度编码的字节结构!换句话说,这种类型的编码既可以使用1个字节,也可以使用2个字节,3个字节,以及4个字节,那么就来了一个问题,我怎么知道这个编码到底是用了几个字节呢?到底是1个字节,2个字节还是3个、4个字节呢?也就是如何进行解析呢?

这里,以GB18030编码为例进行说明,它也是一种变长编码,有1个字节,2个字节以及4个字节大小的编码,下图为GB18030编码的字节结构示意图:

从图中可以很容易地看出:

  1. 前8位为00-80大小的,只有一个字节
  2. 前8位为81-FE,9~16位位40-FE的有两个字节,基本就是兼容GBK编码(但是和GBK还是有区别的,详细区别读者可以自行查阅)
  3. 前8位位81-84,9-16位为30-39的,17-24位为81-FE的,25-32位为30-39的有4个字节!
  4. ……
  5. 根据表格,按照上述的字节区间编码结构就可以进行解码了。

4个字节可以表示更多的字符。

其实GB是国标的意思:

国家标准GB18030-2000《信息交换用汉字编码字符集基本集的补充》是我国继GB2312-1980和GB13000-1993之后最重要的汉字编码标准,是我国计算机系统必须遵循的基础性标准之一。
GB18030-2000编码标准是由信息产业部和国家质量技术监督局在2000年3月17日联合发布的,并且将作为一项国家标准在2001年的1月正式强制执行。 GB18030-2005《信息技术中文编码字符集》是我国制订的以汉字为主并包含多种我国少数民族文字(如藏、蒙古、傣、彝、朝鲜、维吾尔文等)的超大型中文编码字符集强制性标准,其中收入汉字70000余个。

也就是说,这一个标准是我国制定的,并没有在国际上通用!它只编码了我国的汉字以及少数名族文字等。


5.3 Unicode编码

Unicode 其实是一个字符集,这个字符集给世界上常用的字符都进行了编码,每一个字符对应一个唯一的编码。但值得注意的是,它并不是一个字符编码,Unicode还需要依靠一些字符编码规范,才能发挥作用,后面会提到。Unicode 字符集的编码范围是 0x0000 - 0x10FFFF,相比于上面提到的字符编码标准(带GB的都是国标的汉语拼音首字母,因此都是国内的标准),Unicode是一个国际化的标准。换句话说,如果说GB2312、GBK、GB18030是国家级的字符编码,那么Unicode就是一个国际级的字符集!

从上面提到的Unicode的范围可以看出,如果直接编码,我们只需要三个字符就可以编码它。但是,比如第1个字符,如果用3个Byte进行编码,那么它的编码应该是0x000001,问题来了,前面的0并没有包含什么信息,本来1个字节可以存储的,却消耗了3个字节,这是一种存储空间,以及计算机效率的浪费!

因此这里同样采用边长编码,这也就解释了上面为什么Unicode是字符集,而不是一种字符编码了,因为如果直接使用它进行编码会浪费大量的空间和时间

Unicode的编码规则对应utf-8、utf-16、utf-32,每个都代表一种不同的编码规则,utf是Unicode transform format的缩写,Unicode变换格式的缩写。

  • utf-8编码:是一种边长编码规则,可以使用1~4个字节,具体地说:

    • 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的, 所以 UTF-8 能兼容 ASCII 编码,这也是互联网普遍采用 UTF-8 的原因之一。
    • 对于 n 字节的符号( n > 1),第一个字节的前 n 位都设为 1,第 n + 1 位设为 0,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
    • 那么紧接着就有一个问题,没有占满怎么半,答案是补0,从右往左占,高位占不满的自动补0。
    • 还有1个问题,不够位数怎么半,这个问题好解决,增加字节就是了。4个字节最多可以表示3+6+6+6=21,仔细想一下,刚好覆盖Unicode 字符集的编码范围是 0x0000 - 0x10FFFF,没有任何问题。
  • utf16编码:它也是一种变长编码规则,但是它将字符编码成2字节或者4字节。
    • 对于 Unicode 码小于 0x10000 的字符, 使用 2 个字节存储,并且是直接存储 Unicode 码,不用进行编码转换;
    • 对于 Unicode 码在 0x10000 和 0x10FFFF 之间的字符,使用 4 个字节存储,这 4 个字节分成前后两部分,每个部分各两个字节,其中,前面两个字节的前 6 位二进制固定为 110110,后面两个字节的前 6 位二进制固定为 110111, 前后部分各剩余 10 位二进制表示符号的 Unicode 码 减去 0x10000 的结果。20位bit正好可以表示0xFFFF。
    • 大于 0x10FFFF 的 Unicode 码无法用 UTF-16 编码。
  • utf-32编码:UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 码即可,不需要任何编码转换。虽然浪费了空间,但提高了效率。

六、正确的中文字符串遍历方式

从图中可以看到非妃是公主5个字的汉字字符,但是,无论size和length都是10,这说明:length()和size()返回的并不是字符串的长度,而是字符串占用了多少个Byte。

进一步推测:s[i]指的是第i个Byte,it++也指的是前进1个Byte。

而GBK和GB13080都对GB2312向下兼容,而GB2312就包含了汉字中绝大多数,部分生僻字和繁体字是不包含的,GB2312是用2个字节进行表示的。

因此,这里i和i+1才能表示1个字符。

而2个Byte就不能用char(1个Byte)来表示了,string底层是由char实现的,而汉字至少包含两个char的大小,所以要继续用string来表示一个汉字:

遍历算法应该如下:

void test3() {string str = "非妃是公主";cout << "这时test3:";for (int i = 0; i < str.size(); i = i + 2) {string tmp = "";tmp = tmp + str[i] + str[i + 1];if (tmp == "非") continue;cout << tmp;}cout << endl;
}

执行结果如下:

从输出结果中可以看出if (tmp == "非") continue;已经被执行了。

同时,经过调试可以发现,因为字符串的==运算符应该是经过重载生成的,所以在调试时显示没有与操作数匹配的“==”运算符,无法进行监视。

但是从图中可以看出,tmp的值已经是“非”了。也实现了预期的结果,进而可以实现字符串遍历中对单个中文字符串的操作。

同时也在交流群里向大佬交流了一下,大佬帮忙给找了一个参考代码,此处一并贴出,并已标明出处[5],我在这里加上注释,对代码进行解释,如下:

string text = "今天周五123";
for(size_t i = 0; i < text.length();)
{int cplen = 1;if((text[i] & 0xf8) == 0xf0) cplen = 4;   // 占用4个字节,前5位为11110else if((text[i] & 0xf0) == 0xe0) cplen = 3; // 占用3个字节,前4位为1110else if((text[i] & 0xe0) == 0xc0) cplen = 2; // 占用2个字节,前3位为110// 个人感觉这行代码好像没什么用,如果三种情况都不符合,那么cplen就为初始化的0,是符合utf-8编码定义的if((i + cplen) > text.length()) cplen = 1;cout << text.substr(i, cplen) << endl;i += cplen;
}

其实2个Byte基本已经可以表示大多数中文了,除了极少的繁体字和生僻字,但是上面的代码包含了3个Byte和4个Byte的情况,感叹大佬的代码确实更加完善!

最后还要感谢yyl1025老哥的答疑,问题已采纳![6]


六、参考资料

[1] https://zhuanlan.zhihu.com/p/453675608
[2] https://zhuanlan.zhihu.com/p/427488961
[3] https://baike.baidu.com/item/ASCII/309296
[4] https://baike.baidu.com/item/GB18030/3204518
[5] https://stackoverflow.com/questions/40054732
[6] https://ask.csdn.net/questions/7874166


the end……

关于字符编码详解及利用C++ STL string遍历中文字符串的内容到这里就要结束啦~~到此既是缘分,欢迎您的点赞评论收藏关注我,不迷路,我们下期再见!!

字符编码详解及利用C++ STL string遍历中文字符串相关推荐

  1. 字符编码详解及由来(UNICODE,UTF-8,GBK)

    字符编码详解及由来(UNICODE,UTF-8,GBK) 各种字符编码方式详解及由来(ANSI,UNICODE,UTF-8,GB2312,GBK) - 2009-01-29 09:53     一直对 ...

  2. Python2.7字符编码详解

    Python2.7字符编码详解 目录 Python2.7字符编码详解 声明 一. 字符编码基础 1.1 抽象字符清单(ACR) 1.2 已编码字符集(CCS) 1.3 字符编码格式(CEF) 1.3. ...

  3. 转1:Python字符编码详解

    Python27字符编码详解 声明 一 字符编码基础 1 抽象字符清单ACR 2 已编码字符集CCS 3 字符编码格式CEF 31 ASCII初创 311 ASCII 312 EASCII 32 MB ...

  4. 可能是最详细的字符编码详解

    Created By JishuBao on 2019-04-02 12:38:22 Recently revised in 2019-04-03 12:38:22   欢迎大家来到技术宝的掘金世界, ...

  5. Python字符编码详解

    Python字符编码详解 转自http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html Python字符编码详解 本文简单介绍了各种常用的 ...

  6. 字符、字符集和字符编码详解(一文扫清疑惑)

    前言 字符.字符集和字符编码时常看见,之前也看过一些博文,看得迷迷糊糊地,看过即忘,今天有幸碰到一篇能让我醍醐灌顶的文章,整理一下相关知识点与大家分享! 原博文地址:字符集编码详解(学习,看一篇就够了 ...

  7. java字符编码详解_Java中字符编码格式详解

    一.前言 在分析Comparable和Comparator的时候,分析到了String类的compareTo方法,String底层是用char[]数组来存放元素,在比较的时候是比较的两个字符串的字符, ...

  8. java中文乱码解决之道(二)—–字符编码详解:基础知识 + ASCII + GB**

    原文出处:http://cmsblogs.com/?p=1412 在上篇博文(java中文乱码解决之道(一)-–认识字符集)中,LZ简单介绍了主流的字符编码,对各种编码都是点到为止,以下LZ将详细阐述 ...

  9. 字符编码详解——彻底理解掌握编码知识,“乱码”不复存在

    每一个程序员都不可避免的遇到字符编码的问题,特别是做Web开发的程序员,"乱码问题"一直是让人头疼的问题,也许您已经很少遇到"乱码"问题,然而,对解决乱码的方法 ...

最新文章

  1. [转载] 七龙珠第一部——第095话 悟空对抗克林
  2. 秋招 百度二轮面试---血淋淋的经历写实
  3. 微软成功测试氢燃料电池,为数据中心连续供电 48 小时
  4. 我所理解cocos2d-x 3.6 lua --使用Cocos Studio
  5. 机器学习理论《统计学习方法》学习笔记:第二章 感知机
  6. ajax post 提交无法进入controller 请求200
  7. c3p0连接池的配置和简单使用
  8. C语言之文件读写探究(六):fscanf、fprintf(格式化读写文件)
  9. 阶乘末尾蓝桥杯java_Java实现第九届蓝桥杯阶乘位数
  10. 4 图像处理基础知识
  11. C#创建单链表,翻转单链表
  12. matlab得到小波参数,MATLAB|高频信号的小波分析技术要点
  13. 在Mac下使用PanDownload完美下载BD云盘资源
  14. 无利不起早:理性看待IBM倾“芯”中国
  15. 瓜瓜的时空旅行,第三次模拟赛,dfs序+线段树维护最小值
  16. BFS和DFS算法原理(通俗易懂版)
  17. mysql盲注脱裤_BT5下使用SQLMAP入侵加脱裤 -电脑资料
  18. 什么是独立IP,独立IP主机怎么样?
  19. 数据“成精”究竟有多可怕?网络怎么知道我快秃了?
  20. Python数据容器、list列表、tuple元组、str字符串、数据容器(序列)切片、set集合、dict字典、字符串大小比较

热门文章

  1. Type-Script使用详解
  2. 中学生学习方法 摘自360问答
  3. 海外开发者推荐:10个顶级2D游戏资源站
  4. 中介者模式在游戏开发中的应用
  5. python正弦函数
  6. [记录学习]自学尚硅谷张天禹Vue2+3框架_vue_test
  7. 各大网站视频自动播放代码(优酷、搜狐、酷6、土豆等)
  8. 浅浅预告,BIM建模助手【周年庆】让你薅到尽
  9. Heisenberg model建模思路
  10. 使用蒙塔卡罗方法计算定积分(Python)