以下内容转自博客:http://blog.chinaunix.net/uid-22670933-id-1771613.html

一、字符编码是怎么回事

0. 概念

字节是计算机的最基本存储单位,一个字节包括8个位.

字符是一种文字的基本单位,比如'A' 是一个字符,'汉' 也是一个字符.

1. 计算机被发明之后,程序员们编写了很多复杂的计算让计算机运行.

但是一个问题是,计算机如何把辛苦计算的结果告知程序员? 假设计算机把计算结果放在某个寄存器,内容是 1010010

总不能让程序员去检测每个引脚的电位吧? 还是得有个显示器.

显示器是依靠点阵来显示图像的. CPU必须告诉显示器,当CPU 把一个字节的数据比如 00101010 放入显示器寄存器的时候,显示器要显示怎样的一个点阵(图像),这个图像就是我们人类可以看得懂的字符. 这样问题就解决了,比如当CPU把00110001放入显示器寄存器时,显示器就显示(控制点阵画出一个图像-字体)字符 "1", 这是一个查表的过程,内存中的值(内码)和字符是一一对应的. 问题是这个对应关系是可以自由确定的,我可以指定显示器把 00110001(内码) 显示为字符 "1",也可以指定显示为字符"2". 这样当然会引起混乱,同一个内码被映射为不同的字符,不利于人们的交流.

美国国家标准学会(ANSI)决定着手解决这个问题, 英语有一个很小的字符集,26个字母再加上一些控制字符和标点符号,7位2进制值就足以表示所有的变化.于是ANSI公布了一个标准的对应关系,以字节为单位. 当内码为 0110001 时,大家都公认它代表字符 "1",在所有显示器都显示为同一个字符. 这样大家就可以按照同一个标准相互交换数据而不会引起误解. 这个表就是一个包含了128项的对应关系, 叫做 "ASCII", 美国信息交换标准代码.

2.对于中国这样不使用ABC字符的国家来说,如何显示自己的文字是一个大问题.

我们可以制定一个内码表,指定一个内码对应一个汉字. (由于中文的字符非常多,所以一个字节是不够的,至少也要有2个字节存储一个内码.) 这是很容易的,只要国家公布一个标准的内码字符对应表,大家都遵照这个表就可以了.但是还是有一些问题要注意:

(1). 即使在中国,计算机还是得能显示英文吧?

而英文的内码已经有 ASCII 标准在先,并且已经有无数的程序已经在这个标准上运行了很多年,成为不可或缺的部分. 所以我们新制定的内码表必须和 ASCII 兼容.

(2) 很多C语言的库函数是以内码0作为字符串结束标志的,为了兼容那些以前就已经编写好,并且运行良好的程序,我们指定的内码中不能含有值为0的字节.

巧合的是,所有的ASCII内码的最高位都为0. 那么我们只要让第一个和第二个字节(一个汉字占用2个字节)的最高位都为1,这样既和ASCII内码区分开来,又不会出现0.符合这个规则的内码(2字节长)理论上一共可以标识 127 * 127 = 16129个字符(实际上只用了6000多个码位,保留了一些,不过也已经够用了,常用的中文字符只有4000多个).

我们国家公布的这个内码标准表就是GB2312.

原有的英文软件可以很好的运行,C的库函数也不用做修改, 比如 strlen("ABC") 在GB2312表示的内码中, 由于GB2312对英文字符的编码是和ASCII完全一样的,所以返回

3.对于 strlen("A汉字"), 由于strlen()是以内码为0作为边界的,而所有中文字符的GB2312内码高位都为1,不会出现0,并且每个汉字占用2个字节,所以 strlen 返回5. 对于程序来说只要检查一个字节的最高位,就可以很容易的判断这个字符是中文还是英文字符,非常方便.

"一个字母一个字节,一个汉字2个字节" 的观念深入人心.

有了GB2312之后,汉字显示/存储/交换就基本上没什么问题了.

几乎所有的非英语国家都制定了和GB2312类似兼容ASCII的内码字符对应表.

(BIG5 由于有几个字符的内码和ASCII相同但表示不同的字符,不符合2.(1)条件.所以被认为是有"瑕疵"的.)

3. 很明显,GB2312的码位是不够的, 一个例子就是有很多人的人名电脑里打不出来.(只有6000多个码位,而<<康熙字典>>就收录了4万多个汉字).所以后来有出现了诸如GBK, GB18030以及同期流行于台湾香港的BIG5编码. 虽然编码有些不同, 但是设计思想是一致的: 兼容ASCII,并确保不会有某个字节值为0的内码出现.有一个共同的特点是: 它们都是局部的标准,只流行于某个地区/国家内.

4.由于内码表都是各个国家独自制定的,同一个内码,在不同的国家表示的可能是不同的字符.(除了ASCII字符, ASCII字符在所有国家指定的内码表中都有同样的值.)不利于国家间的信息交换. 于是 UNICODE 应运而生.

UNICODE 采用一种很简单的办法来解决这个问题. 就是采用2个 - UCS-2 (或者4个字节 - UCS-4)字节标识一个字符. 2个字节总共可以表示65535个字符,足够表示世界上的所有语言的所有字符.(汉字不就有4万多个吗,65535怎么够. 我估计只是常用的汉字几千个被编在UCS-2中吧. 目前被正式编码到UNICODE码位的只有不超过65534个, 所以就目前的情况来说,用2个字节是可以的.) 注意 UCS-4, UCS-2 和 ASCII是向下兼容的,只要前面补0就可以了.这点很重要,可以一直扩展下去包含全宇宙的字符.

现在地球上每个字符在所有采用UNICODE字符编码的计算机内都有一个唯一的内码了.

要注意, 除了ASCII字符外,其他国家文字的字符的内码是重新分配过的,不一定和各国原有的编码相同.比如大部分汉字的GB2312内码和UNICODE 内码都是不同的.

5. 很显然,对于英语国家来说,UNICODE内码非常浪费空间,对于UCS-2 浪费了50%的存储空间,对于 UCS-4 则浪费了70%的存储空间. 而且还有一个更大的问题, UNICODE的内码中含有很多 '\0', 原有的C标准库函数没办法处理这些字符串.于是有人发明了一种针对UNICODE的变换规则,把UNICODE字符串中的0去除. 注意这个变换规则不是通过查表实现的,而只要用一些位移操作就可以实现. 这就是UTF8.

总结:

UTF8 只是 UNICODE内码在存储/传输时的状态. 而从GB2312编码转换到UNICODE编码需要查表. UTF8 和 UNICODE 的关系 与 GB2312 和 UNICODE的关系有本质的不同. UTF8 和 UNICODE 是一个人的两个面孔, GB2312 和 UNICODE 是两个人.

所以,要实现UTF8编码到GB2312编码的转换必须先把 UTF8编码还原为UNICODE编码,再通过查表的方式,把UNICODE编码转化为GB2312编码.

以上,虽然说得不是很严谨(比如GB2312其实是区位码,真正的内码还要给每个字节加上A0, 这些我都没提,免得分散注意力),但是文字编码的原理大致就是那么回事,理解就好了. 要想详细了解细节Google一下能找到很多资料.

二、字符编码的编程相关问题

1. Windows从NT开始,内核使用UNICODE内码. 为了向前兼容,前端使用的还是GB2312内码(中文环境).

所以用 Visual Studio 编写代码时, 如果在CPP文件中写这样一句 const char* pszText = "中文", 编译器让 pszText 指向"中文"的GB2312内码值的内存空间. 当调用 printf(pszText)时, WINAPI 把这个GB2312字符串转化为UNICODE字符串再输出.(WIndows自然知道你的编码是GB2312,因为你在Windows系统中设置的语言区域是中国, CodePage 936. 如果改成其它语言,就会显示为乱码.)

微软非常鼓励Windows程序员用Unicode编写程序,很明显,由于Windows内核就是原生的Unicode环境,调用API时,省却了编码转换的操作,效率更高. 而且一个额外的好处就是不会有乱码. 注意,MS的C/C++编译器把sizeof(wchar_t)设置为2个字节. 由于目前所有的UNICODE字符只有65534个码位(BMP),所以用2个字节是没问题的.

2. Linux系统(比如Ubuntu)现在一般都用UTF8编码了.

我们在Linux下创建CPP文件并添加同样的: const char* pszText = "中文" 编译器会让 pszText 指向"中文"UTF8的内码值的内存空间.Linux的终端可以理解为一个只接收UTF8字符串的显示器. 任何被写到终端的字符流都被认为是是一个UTF8字符流.所以,编程的时候,从外部(文件或者控制台)读入UTF8字符流,转换为wchar_t,然后程序在内部使用宽字符处理,最后再把要输出的宽字符流转换为UTF8字符流并输出到控制台/文件中. 用户程序可以通过环境变量LANG的值得知当前的系统环境所使用的字符编码.由此可见,C库函数的 mbstowcs()/wcstombs()主要是为应付这种情况设计的. 如果要处理XML, HTML 等等有明确指明字符编码的字符流,用专门的字符转换库更为方便.

为什么很多Windows下的C源文件的注释在Linux编辑器下会显示为乱码就很好理解了.

3. 字符编码转换相关的函数和库

Windows 的字符转换函数: MultiByteToWideChar() / WideCharToMultiByte()

Linux 的字符转换库: GLIBC iconv函数组.
C标准库使用的 mbstowcs()和wcstombs()和 locale 相关,用起来很不方便,而且功能有限.

(注意不要假设 wchar_t 的大小, 它可能是4字节也可能是2字节,取决于编译器. 比如 MS VC9.0 (2008) 里, sizeof(wchar_t) = 2, 而在GCC中, sizeof(wchar_t) = 4.)

4. 给定一个ANSI兼容的字符串(包括GB2312,GBK,UTF8等),无法确定它的编码类型,只能猜测.所以不要指望会有一个万能的转换函数.

5. BOM (Byte Order Mark)UNICODE: FF FE / FE FF 和 UTF8: EF BB BF 是不完全靠谱的,仅供参考.

最后说明一点,对于不是专门处理字符编码的程序来说,所有字符编码相关的问题只是显示的问题,并不会影响到程序的内在逻辑.

开始用 Unicode 来编写我们的代码吧.

转载:从程序员的角度看ASCII, GB2312, UNICODE, UTF-8的更多相关文章

  1. C# 中重载自增自减操作符的具体运算原理 ----从C++程序员的角度看C#自增操作符重载的实质

    看了看C#的运算符重载,发现与C++打不相同.刚刚被C#的自增操作符坑了,现在来分享一下. 先定义一个类 class A { public int i; public A(int I) { i = I ...

  2. 从程序员的角度分析微信小程序(编程语言:用到什么学什么)

    从程序员的角度分析微信小程序(编程语言:用到什么学什么) 一.总结 一句话总结:微信小程序原理就是用JS调用底层native组件,和React Native非常类似.(需要时,用到时再学) 1.选择语 ...

  3. 从程序员的角度分析微信小程序

    昨天朋友圈被微信小程序刷爆了. 我赶快在书架上拿出三年前买的书,把上面的土擦干净,压压惊. 作为一个并不是资深的程序员. 从程序员的角度分析一下微信小程序,欢迎指点. 首先吐槽 微信小程序只发了200 ...

  4. 从程序员的角度深入理解MySQL

      前言 今天我将站在程序员的角度以MySQL为例探索数据库的奥秘! 数据库基本原理 我对DB的理解 1.数据库的组成:存储 + 实例 不必多说,数据当然需要存储:存储了还不够,显然需要提供程序对存储 ...

  5. 程序员收藏必看系列:深度解析MySQL优化(二)

    程序员收藏必看系列:深度解析MySQL优化(一) 性能优化建议 下面会从3个不同方面给出一些优化建议.但请等等,还有一句忠告要先送给你:不要听信你看到的关于优化的“绝对真理”,包括本文所讨论的内容,而 ...

  6. 2.数码相框-编码(ASCII/GB2312/Unicode)介绍

    转载:https://www.cnblogs.com/lifexy/p/8485634.html 在上章-学习了数码相框的框架分析(1)了 本章主要内容如下: 1)熟悉ASCII/GB2312/Uni ...

  7. 2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

    在上章-学习了数码相框的框架分析(1)了 本章主要内容如下: 1)熟悉ASCII/GB2312/Unicode编码 2)写应用程序,使LCD显示汉字和字符 大家都知道,数据传输的是二进制,而字符和汉字 ...

  8. 这本最适合夯实基础的经典 Java 书籍,可能80% 的 Java 程序员没有认真看过!

    公众号[程序员书单]出品,转载请注明出处 作者:黄小斜 今天要给大家带来的一本书,是大名鼎鼎的head first系列丛书的一本<head first Java>相信很多学习Java的朋友 ...

  9. <转载> 优秀程序员必备的23条好习惯

    转自 优秀程序员必备的23条好习惯 编程是一项聪明人玩的游戏,它既是对智力的考验,也是对习惯的考验,智力的好坏取决于父母的基因,人们无从左右,但习惯的好坏却是可以不断培养.一项由美国芝加哥大学国家研究 ...

随机推荐

  1. 不开vip会员照样看vip电影(亲测有效)

    此为临时链接,仅用于文章预览,将在短期内失效关闭 不开vip会员照样看vip电影(亲测有效) 2018-03-08 mr_lee Python达人课堂 刚刚测试,真实有效,颇不接待要分享了... 土豪 ...

  2. PLSQL计算质数

    看到有人实现了一个计算质数的函数,就是效率有点差,贴一个以前写的计算质数的算法. 目标很简单,列出100以内的质数.其实算法很简单,两个循环就搞定了.但是发现使用不同的算法,执行效率差别之大相当惊人, ...

  3. < Python Index >

    1. 基本语法   1.1 常量/变量   1.2 运算符   1.3 表达式   1.4 程序结构 2. 内置数据结构    2.1 列表    2.2 元组    2.3 集合    2.4 st ...

  4. 把AspDotNetCoreMvc程序运行在Docker上-part3:使用独立的存储容器

    接上一篇博文<把AspDotNetCoreMvc程序运行在Docker上-part2:修改容器以及发布镜像>,这次我们看看如何使用docker存储数据. 背景 之前的示例都只有一个网站应用 ...

  5. 简单的自定义Session

    有关Session.Cookie机制建议参考文章:CookieSession机制详解,写的很详细,不再赘述 本篇文章通过一个简单的案例揭秘Session机制以及和Cookie的区别和联系: 服务器端代 ...

  6. 遍历FTP目录及下载

    操作ftp,直接在main方法中即可操作. 例1:遍历ftp目录中的文件 public static void main(String[] args) throws IOException { FTP ...

  7. JDK安装与环境变量全过程-鹏鹏

    首先先讲下JDK的含义以及用处: JDK是 Java 语言的软件开发工具包,主要用于移动设备.嵌入式设备上的java应用程序.JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Jav ...

  8. Linux下socket通信和多线程

    服务端socket流程:socket() –> bind() –> listen() –> accept() –> 读取.发送信息(recv,send等) 客户端socket流 ...

  9. python 历险记(五)— python 中的模块

    目录 前言 基础 模块化程序设计 模块化有哪些好处? 什么是 python 中的模块? 引入模块有几种方式? 模块的查找顺序 模块中包含执行语句的情况 用 dir() 函数来窥探模块 python 的 ...

  10. [Clojure] 包管理器leiningen配置国内镜像仓库

    clojure用到的包管理站主要有两个,一个是解决java类库依赖的maven,一个是clojar 很多人都知道maven有阿里云提供的镜像站,可是clojar呢?幸运的是中科大为我们提供了cloja ...