最近在 vscode 中借助 gcc 编译器来配置 c

语言开发环境时,发现中文编码存在乱码问题。再加上最近学习到多字节字符与宽字符,搅在一起,搞得很乱,就把自己的理解写下来,供有需者参考吧。

1. 字符编码

先来看维基中关于字符编码的描述

字符编码

字符编码(英語:Character

encoding)、字集碼是把字符集中的字符编码为指定集合中某一对象(例如:比特模式、自然数序列、8位元组或者电脉冲),以便文本在计算机中存储和通过通信网络的传递。常见的例子包括将拉丁字母表编码成摩斯电码和ASCII。其中,ASCII将字母、数字和其它符号編號,並用7位元的二进制來表示这个整数。通常會額外使用一个扩充的位元,以便于以1个字节的方式存储。

在计算机技术发展的早期,如ASCII(1963年)和EBCDIC(1964年)这样的字符集逐漸成為標準。但这些字符集的局限很快就变得明显,于是人们开发了許多方法来扩展它们。对于支持包括东亚CJK字符家族在内的写作系统的要求能支持更大量的字符,并且需要一种系统而不是临时的方法实现这些字符的编码

关于字符编码的详细介绍,可以参考 字符编码笔记

Windows 现在默认所用的汉字编码仍是 GBK,而 字符编码笔记中没有提及,, 因此以下对 GBK 编码进行相应的介绍。

1.1 GBK 编码

汉字内码扩展规范

汉字内码扩展规范,称GBK,全名为《汉字内码扩展规范(GBK)》1.0版,由中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订,国家技术监督局标准化司和电子工业部科技与质量监督司1995年12月15日联合以《技术标函[1995]229号》文件的形式公布。

GBK共收录21886个汉字和图形符号,其中汉字(包括部首和构件)21003个,图形符号883个。

GBK的K为“扩展”的汉语拼音(kuòzhǎn)第一个声母。英文全称Chinese Internal Code Extension

Specification。

字符有一字节和双字节编码,00–7F范围内是第一个字节,和ASCII保持一致,此范围内严格上说有96个文字和32个控制符号。

之后的双字节中,前一字节是双字节的第一位。总体上说第一字节的范围是81–FE(也就是不含80和FF),第二字节的一部分领域在40–7E,其他领域在80–FE

也就是说在 GBK 编码中

  • 对于单字节的字符,字节的第一位为 0,后面 7 位为这个符号的 Unicode 码
  • 对于双字节的字符,字节的第一位为1, 后面的第二位要遵循上面提及的规则

2. 多字节字符与宽字符

由上面关于编码的介绍可知,一个字符可能占据一个字节,也可能占据两个字节。由于字符在实际储存时,都是二进制的格式,因此需要借助额外的信息才能判断出字符的实际字节数。如,对于GBK编码来说,其首位为 0,说明其只有 1 个字节;首位为 1,则说明其有两个字节。

为了避免需要额外的信息,才能判断出字符中实际的字节数,则就需要引入宽字符(C语言中的定义为 wchar_t)。c 语言中的宽字符占据 2 个字节,其优点是能够加快字符的解析速度(应为不再需要判断字符的个数),其缺点也显而易见,就是会增加内存空间的占用(因为能在多字节字符中用一个字符表示的字符,用宽字节也必须要用两个字符来表示)

多字节字符和宽字符,有点类似于算法中运算速度和内存占用的问题,鱼与熊掌,不可兼得也。

3. gcc 编译器配置以及相关实例

之所以会对编码进行深入的学习,是因为最近在 vscode 上借助 gcc 来配置 c 语言开发环境时,碰到了汉字乱码的问题,再加了最近在学习宽字符,所以对编码知识进行了深入的学习

所用的 gcc 编译环境如下:

gcc version
8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)

gcc 编译时,默认会按照 c 文件的编码进行编码,所以 c 文件的编码要和 cmd 命令窗口的编码格式相同,不然就会出乱码。

windows cmd 的默认的编码为 gbk,如下图:

因此,c 文件的编码也要为 gbk,才能保证汉字不乱码。当然也可以通过 chcp 65001 来将cmd 的编码格式改为utf-8,这样 utf-8 编码的c 文件输出的汉字就不会乱码。

如果要把 cmd 改为默认的编码,则使用 chcp936命令即可。

当然,如果不想修改默认的 cmd 编码,又想避免由于 c 文件的编码与 cmd 默认编码不匹配所导致的乱码问题,可以在 gcc 的编译选项中加入 -fexec-charset=gbk 来避免乱码。

3.1 多字节字符实例

#include<stdio.h>
#include<string.h> void main()
{
char str[10] = "李";
int a, b, c;
printf("%#X %#X %#X\n", (unsigned char)str[0], (unsigned char)str[1], (unsigned char)str[2]);
printf("length: %d\n", strlen(str));
a = printf("%c%c%c", str[0], str[1], str[2]); // 输出 3 个字节
putchar('\n');
b = printf("%c%c", str[0], str[1]); // 输出 2 个字节
putchar('\n');
printf("a = %d, b = %d", a, b);
}
  • 编译时采用 utf-8 编码

    gcc -fexec-charset=gbk utf8.c -o utf8.exe
  • cmd 窗口采用 utf-8 编码的输出

    • 从图中的结果可看出,连续输出 3 个字节可以输出正确的汉字,这是由于 utf-8 中,常用汉字为 3 个字节。

    • 只输出两个字节时,没法显示内容。从 b = 2 也可以看出,输出成功,说明不能显示的原因是 cmd 解析出错。出错的原因是因为其第一个字节已经指定了当前字符包含 3 个字节,而实际只输出两个字节,导致 cmd 窗口解析失败。

      utf-8 的编码规则

      UTF-8 的编码规则很简单,只有二条:

      1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

      2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

      下表总结了编码规则,字母x表示可用编码的位。

      Unicode符号范围     |        UTF-8编码方式
      (十六进制) | (二进制)
      ----------------------+---------------------------------------------
      0000 0000-0000 007F | 0xxxxxxx
      0000 0080-0000 07FF | 110xxxxx 10xxxxxx
      0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
      0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  • cmd 窗口采用 gbk 编码输出

    • 鏉(shòu 锋利的意思) 对应的 GBK 编码是 E6 9D,因此打印 3 个字节和 2 个字节时,由于 cmd 的显示的编码为 GBK,所以会输出 鏉,跟实际文件中的”李“相比,就是乱码
    • 输出 3 个字节时,E6 9D 会被解析为 鏉。8E 会解析失败,因为其首位大于 1,按照 GBK 的编码规则,其应该有两个字节,而其只有一个字节,因此解析失败,没有输出

3.2 宽字符实例

// 文件名 wide_char.c,编码 utf-8
#include<stdio.h>
#include<locale.h> void main()
{
setlocale(LC_CTYPE, ""); // 设置本地化,不然宽字符无法正常显示
wchar_t wch = L'李'; // 宽字符的定义
wprintf(L"%c\n", wch);
printf("%lc\n", wch); // 输出结果为 2,2 说明一个宽字符占据两个字符
printf("%d %d\n", sizeof(wch), sizeof(wchar_t)); // 编译编码为 utf-8 则输出为 6,5
// 编译编码为 gbk, 则输出结果为 6, 4
// 以上结果说明一个宽字符固定占据两个字节
printf("%d %d", sizeof(L"1李"), sizeof("1李"));
}
  • 编译运行过程

    • 从结果中可看出,在运行的过程中,没有改变 cmd 的编码格式,但是汉字输出没有乱码。

    • c 语言的宽字符借助 Unicode 来实现,因此在使用宽字符时, c 文件的编码格式最好是 utf-8。如果编码格式为 gbk,则编译时会报错

相关网站

gbk 编码查询

utf-8编码查询

字符编码与gcc 编译器的编码问题的更多相关文章

  1. gcc编译器对宽字符的识别

    最早是使用VC++工具来学习C++,学的越多就越对VC挡住的我看不见的东西好奇,总想多接触一些开发环境,今日抽空摸索了一下CodeBlocks这个开源的IDE使用方法,配置的编译器是MinGW的gcc ...

  2. C++中宽字符类型(wchar_t)的编码

    转载自: http://www.ituring.com.cn/article/111027 问题的起因是和一个朋友讨论不同编码的转换问题,说到了wchar_t的类型,朋友的看法是,wchar_t的编码 ...

  3. Eclipse设置软tab(用4个空格字符代替)及默认utf-8文件编码(unix)

    简单配置版本: Eclipse設置 一.window->Preferences-> General-Editors->Text Editors , 右边勾选insert spaces ...

  4. 【开发技术】Eclipse设置软tab(用4个空格字符代替)及默认utf-8文件编码(unix)

    Eclipse设置软tab(用4个空格字符代替)及默认utf-8文件编码(unix) 本文摘要: 1.如何配置Eclipse中编辑器支持softtab(用数个空格字符代替默认的tab缩进): 2.如何 ...

  5. 字符编码 + python2和python3的编码区别(day08整理)

    目录 昨日回顾 二十三.元组内置方法 二十四.散列表 二十五.字典内置方法 二十六.集合内置方法 二十七.深浅拷贝 拷贝 浅拷贝 深拷贝 今日内容 二十八.字符编码 1.文本编辑器存储信息的过程 2. ...

  6. gcc编译器与基本类型3

    C语言发展史 1969年贝尔实验室 肯尼斯·蓝·汤普逊,丹尼斯·李奇开发了B语言 ->Unix,New B语言,改名C语言83年提出C语言标准 1989年十二月正式通过C语言标准,C89标准 C ...

  7. 谈谈字符集编码及gb2312、utf-8编码原理

    一.基础中的基础比特位即bit,是计算机最小的存储单位.以0或1来表示比特位的值.Byte是字节数,bit是位数,在计算机中每八位为一字节,也就是1Byte=8bit:Byte和bit都翻译成比特,俗 ...

  8. 你还在为如何区分ASCII编码、GB2312编码、Unicod、UTF-8编码而烦恼吗,一篇文章让你柳暗花明

    字符编码 我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特 ...

  9. gcc编译器的工作流程

    参考资料:http://www.cnblogs.com/dfcao/p/csapp_intr1_1-2.html 在linux系统上,从源文件到目标文件的转化是由编译器完成的.以hello.c程序的编 ...

随机推荐

  1. logback使用配置

    一:logback.xml配置内容如下 <?xml version="1.0" encoding="UTF-8"?> <!-- Copyrig ...

  2. Java内存模型的基础

    Java内存模型的基础 并发编程模型的两个关键问题 在并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在 ...

  3. 浅谈Ceph纠删码

    目  录第1章 引言 1.1 文档说明 1.2 参考文档 第2章 纠删码概念和原理 2.1 概念 2.2 原理 第3章 CEPH纠删码介绍 3.1 CEPH纠删码用途 3.2 CEPH纠删码库 3.3 ...

  4. main方法中注入Spring bean

    在有些情况下需要使用main使用Spring bean,但是main方法启动并没有托管给Spring管理,会导致bean失败,报空指针异常. 可以使用 ClassPathXmlApplicationC ...

  5. Winform DataGridView 取消默认选中行

    困境 网上有很多解决方法,可是很多读者照做并不生效.追究其原因,问题出现在许多博主没有搞清楚DataGridView绑定与当前触发事件的关系. 复现 private void Frm_Load(obj ...

  6. 编码规范 | Java函数优雅之道(上)

    导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一.软件系统思维有句话“Less coding, ...

  7. 【算法】【排序】【交换类】快速排序QuickSort

    #include<stdio.h> //快速排序 int main(){ ,,,,,,,,}; +; //基准指针 ; //慢指针 int* j=a; //快指针 int QS(int* ...

  8. The introduction of the book American daily English notes (enlarged edition)

    After reading the book of American daily English notes written by Linkun Yang[1], I think I should a ...

  9. Transformation HDU - 4578(线段树——懒惰标记的妙用)

    Yuanfang is puzzled with the question below: There are n integers, a 1, a 2, …, a n. The initial val ...

  10. ES解决bootstrap checks failed, memory locking requested for elasticsearch process but memory is not locked问题

    问题描述: ERROR: [1] bootstrap checks failed[1]: memory locking requested for elasticsearch process but ...