# 字符编码 字符编码即是**将来自各种语言文字系统中的==字符和符号映射为唯一对应的数字==**,从而能够**在计算机中进行存储、表示和传输**。 实现字符编码包括两个部分: - **==字符集==(Character Set)**:定义了一组字符以及它们对应的编号:**==字符->数字==的映射** - **ASCII 码**:单字节 7 位二进制数表示,共 128 个字符的编码。只占一个字节中的后 7 位。 - **Unicode**:可容纳世界上所有文字和符号的字符编码方案 (**字符集**)。只规定了**字符的二进制码**,未规定其在计算机中的存储/表示方式。 - **==编码方案==(Encoding Scheme)**:定义了**如何将==字符集中的数字编号==转换为计算机存储的字节序列**。 - **UTF-8**:对 Unicode 字符集的一种编码方案,具有向后兼容 ASCII 的特性,是计算机中最常用的编码方式, - **GB2312/GBK**:中文常见的编码方式,两个字节表示一个字符,理论上可表示 $2^{16}=65536$ 个字符。 > [!NOTE] > > windows10, 11 默认的系统或者终端的编码所在区域有关(比如大陆的 **GBK** ); > > 在我的笔记电脑上,终端编码还是默认 **GBK**, 而记事本等软件保存的默认编码使用的是 **UTF-8** 。 <br><br> # ASCII 码 ASCII码(American Standard Code for Information Interchange,美国信息交换标准代码) **标准 ASCII 码**一共定义了 **128 个字符**,使用 **7 位二进制数**($[0, 127]$)来表示,码表包括: - **控制字符**:如回车、换行、制表符等,范围为 $[0,31]$ 以及 $127$,共 33 个字符 - 0(NULL,空字符) - 7(BEL,响铃) - 8(BS,退格) - 9(HT,水平制表符) - 10(LF,换行) - 13(CR,回车) - **可显示字符**:字母、数字、标点符号、空格,范围是 $[32,126]$,共 99 个字符 - **32(空格)** - **48–57(0到9)** - **65–90(A到Z)** - **97–122(a到z)** - 33–47、58–64、91–96、123–126(标点符号和特殊字符) > [!NOTE] `\r\n` > > `\r` 是 ASCII 回车符,**`\n` 是 ASCII 换行符**。 > ASCII 码表示的字符数量有限,仅涵盖英文大小写和阿拉伯数字,**不足以表示全球所有语言的字符和符号**。 为此,**采用了更广泛的字符编码标准如 Unicode 编码**。 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-0701D0851668BB2B4D99592DB67ADBD8.png|525]] ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-8A814216FC0824DD5BF38B99E5D5FD7D.png|625]] <br><br> # Unicode 码 **Unicode**是一个**全球性的字符集标准**,为**世界上几乎所有的文字字符和符号**提供了**唯一的数字标识**(称为代码点),从而确定了一个统计的标准,其**码表涵盖全球范围内各种语言的字符**,也称之为 "**统一字符集**"。 > [!info] Unicode 码与 UTF-8/16/32 之间的关系 > > - **Unicode 是一个==字符集==**(字符与 Unicode 码的对应关系,类比于 ASCII 码) > > > > > - **UTF-8/16/32 编码方式**是**针对 "==Unicode 码==" 的具体编码方案**,定义了 **==字符的具体存储方式==**,即**用多少个字节,怎样的编码方式(可变长度 or 定长)来"==编码=="一个 Unicode 码**。 > > > 对字符编码的过程为: > > $ > 字符 \stackrel{映射}{\longrightarrow}\text{Unicode 码}\stackrel{编码}{\longrightarrow}\text{UTF-8/16/32编码} > $ > <br> ## Unicode 字符集 **Unicode字符集简写为UCS (Unicode Character set)** - UCS-2,即2字节编码(4位十六进制数),涵盖 Unicode 码范围为 `0x0000 ~ 0xFFFF`。 - UCS-4,即4字节编码(8位十六进制数),涵盖 Unicode 码:`0x000000~0x10FFFF`。(实际内容只有3字节范围) UCS-2只涵盖了65535个字符,而**UCS-4涵盖上百万个字符**。 <br> ## Unicode 编码方案 **Unicode 编码的实现方式:Unicode Transformation Format** Unicode 的**编码方式**指的是——**对字符的 Unicode 码进行==编码并实际存储表示==的方式**,包括: - **==UTF-8==** - 一种**可变长度**的编码方式,使用**1到4个字节**来表示一个 Unicode 代码点。 - 具有向后兼容ASCII的特性:**ASCII字符在UTF-8中的表示与其原始表示相同** - **UTF-16** - 一种**可变长度**的编码方式,使用**1或2个16位代码单元(2或4个字节)** 来表示一个 Unicode 代码点。 - **UTF-32** - 一种**固定长度**的编码方式,**每个Unicode代码点**都**直接映射到一个32位**的整数。 - 简化了字符的处理,但相比UTF-8和UTF-16占用更多的存储空间,因此在需要节省空间的应用中较少使用 ### (1)UTF-8 对Unicode码的**变长编码方式**,用**1~4个字节**表示一个符号,对Unicode中不同范围的字符使用不同长度的编码。 **编码规则**: - 对于单字节的符号,字节第一位设为 0,后7位为该符号的 Unicode 码。因此对于Unicode中0x00-0x7F之间的字符,UTF-8 编码和 ASCII 码是相同的。 - 对于 n 字节的符号(n>1), 第一个字节的**前 n 位都设为 1,第 n+1 位设为 0**;后面字节的前两位一律设为 10,剩下的没有提及的二进制位,全部为这个符号的 Unicode 码 (依次从前往后填,空余位补 0)。 |Unicode码(十六进制)|UTF-8编码(二进制)| |---|---| |0000 0000 ~ 0000 007F|0xxxxxxx (这一范围的字符,UTF-8与ASCII码完全相同)| |0000 0080 ~ 0000 07FF|110xxxxx 10xxxxxx| |0000 0800 ~ 0000 FFFF|1110xxxx 10xxxxxx 10xxxxxx| |0001 0000 ~ 0010 FFFF|11110xxx 10xxxxxx 10xxxxxx 10xxxxxx| 解码:如果一个字节的第一位是`0`,则这个字节单独就是一个字符;如果第一位是`1`,则连续有多少个`1`,就表示当前字符占用多少个字节。 **例如**:严字的Unicode码为`4E25` (100111000100101),则用UTF-8编码需要3个字节: 1110**0100** 10**111000** 10**100101**, 换为16进制:`E4B8A5`。 可见,**UTF-8是对 Unicode 编码的具体实现方式,其实际编码并不等同于原始 Unicode 码**。 就中文字符而言,用UTF-8编码时,绝大多数占3字节,少数占4字节。 --- <br> ### (2)UTF-16 Unicode码用**两字节或四字节**表示。源于UCS-2,是Unicode最早的编码方式[^1]。 编码规则: - 对Unicode码**U+0000 ~ U+FFFF**范围的用两个字节表示,**直接存储码点,即Unicode号**,不进行编码转换。 - 对Unicode码在**U+10000 ~ U+10FFFF**范围的用四个字节表示,采用某种编码方式进行转换 |Unicode码(十六进制)|Unicode码(十六进制)|UTF-16编码(二进制)| |---|---|---| |0000 0000 ~ 0000 FFFF|xxxxxxxx xxxxxxxx|xxxxxxxx xxxxxxxx| |0001 0000 ~ 0010 FFFF|yyyy yyyy yyxx xxxx xxxx|110110yy yyyyyyyy 110111xx xxxxxxxx| 其中,UTF-16编码时,BOM头用`FE FF`表示大端序,`FF FE`表示小端序。 U+FEFF 字符在 Unicode 中代表的意义是"ZERO WIDTH NO-BREAK SPACE",顾名思义,它是个没有宽度也没有断字的空白。 例如 : 1. 严:Unicode `4E 25`, UTF-16: `4E 25` (Big endian), `25 4E`(Little endian) 。 2. "ABC"字符串在UTF-16中的编码,各种情况如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201231183846359.png) windows下很多软件的编码选项“Unicode”这一不恰当措辞实际上是指(通常有BOM标记)**小端序UTF-16**。 --- <br> ### (3)UTF-32 全部使用四个字节进行编码。编码规则简单,字符的Unicode码点值即为UTF-32的编码值^{[2]}。 UTF-32编码中,用`00 00 FE FF`表示大端序, `FF FE 00 00`表示小端序。 ##### UTF-16/32中的大小端 用Big endian和Little endian两种方式来区分Unicode字节的实际表示顺序。 - big-endian,大端,将高位的字节放在低地址表示。 - little-endian,小端,将高位的字节放在高地址表示。 反序,即将字节反着放。 (从左到右,字节: 高位->地位。地址:低->高) ##### BOM BYTE ORDER MARK, 字节顺序标记,称为BOM头,出现在文本文件的头部,标识文件是采用哪种格式的编码。 **来历:** 为识别Unicode 文件,Microsoft 建议所有的 Unicode 文件应该以 “ZERO WIDTH NOBREAK SPACE“字符开头,用以识别编码和字节标记顺序。Linux/UNIX并未使用BOM。 UTF-8编码不需要BOM表明字节顺序,但可通过BOM表明编码方式为UTF-8。使用的BOM即是`EF BB BF`。 Windows自带的记事本将文件保存为UTF-8编码的时候,记事本会自动在文件开头插入BOM。 <br><br><br> # GB2312/GBK 编码 # 乱码 **在不同字符编码方式下,同一串二进制数字序列可以被解释成不同的符号**。 因此,要想正确地显示一个文本文件的内容,就**必须知道它的编码方式**,否则**如果用不相匹配地编码方式去解读,就会出现乱码**。 ### **烫烫烫/屯屯屯** 在 Windows 下: - **未初始化的栈内存**会被置值为 `0xcc` => **'烫' 的 gbk 编码** - **未初始化的堆内存**会被置值为 `0xcd` => **'屯' 的 gbk 编码** > [!NOTE] "烫" 与 "屯" 乱码的产生原因 > > 在 Visual Studio 中的 Debug 模式下,如果声明一个变量但是没有初始化,**windows 会给未初始化的栈内存复制为 `0xCC`**,如果是堆内存则是 `0xcd`,同时由于 **VS 中调试器默认的字符集是 MBCS**,而在 MBCS 中 `0xCC` 对应中文的“烫”,`0xcd` 则对应中文的"屯",所以会显示出来乱码。 > > 给为初始化的内存赋0xCC 是有原因的,0xCC 其实是 INT3中断指令,所以**如果在 Debug 模式下试图去执行这块未初始化的内存的话就会中断程序**。 ### **锟斤铐** > 乱码源于Unicode字符集和GBK字符集之间的转换问题。 Unicode 中,**0xFFFD 字符是一个特殊字符**,**所有 Unicode 无法表示的字符都会通过一个占位符来表示**。 即“U+FFFD REPLACEMENT CHARACTER“。 **U+FFFD的UTF-8编码**,恰好是 '\xef\xbf\xbd'。如果这个'\xef\xbf\xbd',重复多次,例如 '\xef\xbf\xbd\xef\xbf\xbd',然后放到GBK/CP936/GB2312/GB18030的环境中显示的话,一个汉字2个字节,最终的结果就是:锟斤拷——锟(0xEFBF),斤(0xBDEF),拷(0xBFBD)。 产生过程:**gbk字符被解码成utf-8**,其中**无法被识别的字符被通过U+FFFD替代**,而最后又被解码成gbk即翻译回了汉字。 <br><br><br> # 乱码产生的原因 如果读取和写入时用的编码不同,就可能导致乱码。 例如用GBK编码保存的文件, 其中包含有中文, 则使用utf-8编码打开读取文件时, 就会显示乱码。 例如**文件编码格式**,与**控制台/终端编码**格式不一致,此时就会显示乱码[^2]。 #### CLion中RUN / DEBUG编码错误的原因 windows 系统控制台(Powershell,cmd等)的**默认编码/字符集是GBK**,而CLion对文件的默认编码/字符集是utf-8。由此产生乱码。 CLion中Run启用的终端以系统(控制台)默认编码读取处理,但Debug的编码是UTF-8,不同。 - 程序输出,取决于程序文件的编码 - "请按任意键继续输入" 是控制台日志,由控制台自身输出,采用与控制台一致的编码。 这个思路是, 在运行代码的过程中,将终端的字符集改为utf-8(只对当前终端有效),此时**要求文件本身是`utf-8编码`,如果文件本身是`GBK`,同样会变成乱码。** // 添加system("chcp 65001"); 将终端的字符集更改为utf-8: ​ ```cpp #include <iostream> using namespace std; int main(){    system("chcp 65001");    cout << "hello" << endl;    cout << "你好中国" << endl;    return 0; } ``` `Editor>File Encodings` 下的设置: CLion 根据这些设置来 **查看和编辑** "**无法检测到编码**"的文件,并**对新文件**使用指定编码。 If CLion can't determine the file or directory encodings, it falls back to the configured project encoding. If there is no project, CLion uses the global encoding. **File or directory encodings take precedence over the project encoding, which, in turn, takes precedence over the global encoding.** - Global Encoding 是在其它Encoding 选项未应用时才启用,例如对一个不属于任何项目的单独文件。 - Project Encoding 窗口下表中未配置的文件所采用的默认的编码显示方式。 - `Default encoding for properties files`: 设置对项目中的属性文件的编码方式; 另外: CLion存在Bug。 本来通过文件→设置→编辑器→常规→控制台中的默认编码设置就可以更改CLion控制台的字符集,实际却不可以。 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-CB057BA205F4DF2272152F95FBC71E8D.png|857]] 文件为 `GBK` 编码: ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-972FE13AF811B94AE82F9B9AE39A6EA5.png]] ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-8DEA564F0A701174D763F095D232ADE1.png]] ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-CC75EB30A4CBF7E2D051A461DD2385AF.png]] 文件改成 `UTF-8` 编码: ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-22D9CCF0BD2379451DAE12F2EA7DBDE2.png]] ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-66362641D5926B1B5C0138835DEC7AEC.png]] 文件为UTF-8,而在GBK编码的终端下向程序输入,并让程序输出。 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-EF7CEFC910AAEC1A1725B32072301B12.png]] ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/Unicode字符编码.assets/IMG-Unicode字符编码-134DEFDF3A943D5328B2FD2BFCE6EA04.png]] <br><br> # 参考资料 - [字符编码笔记:ASCII,Unicode 和 UTF-8 - 阮一峰的网络日志](https://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) - 正确转换 Unicode/UTF-8/16/32 的网站:[Unicode编码转换,UTF编码转换(UTF-8、UTF-16、UTF-32)](https://www.qqxiuzi.cn/bianma/Unicode-UTF.php) # FootNotes [^1]: [Unicode编码详解(四):UTF-16编码-CSDN博客](https://blog.csdn.net/hyongilfmmm/article/details/112046816) [^2]: [请问clion的控制台中文乱码问题怎么正确解决呢?](https://www.zhihu.com/question/386494355)