查看原文
其他

烫烫屯屯锟斤拷��

漫话编程 博文视点Broadview 2022-09-09

👆点击“博文视点Broadview”,获取更多书讯


▊你可能看到过国内超市售卖22元一盒的“锟斤拷”!?

▊就连国外也有出售过的“屯屯屯”!?


▊电脑出现“烫烫烫”的时候难道是CPU发热的自救信号?


其实上面这些都是乱码。

但是这些乱码都是怎么来的?我们要从计算机的编码方式讲起。

什么是 ASCII 和 Unicode

1. 字符编码和 ASCII

电报在传递的过程中,需要发报员用电键发出长短不一的电码,收报员听到的是电报机发出的滴滴答答的声音。其实电报发出的声音都是“滴”和“答”的组合,“答”的声音是“滴”的三倍长。

发报员要先通过一种方式,将想要发送的情报转成电报的滴答声,收报员在听到滴答声之后,再将它们翻译成正常的文字。这个过程就是字符编码和字符解码。

谍战剧中将情报转成电报的“滴”声和“答”声主要通过摩尔斯电码实现的,这是一种通过不同的排列顺序来表达不同的英文字母、数字和标点符号的字符编码方式。


摩尔斯电码由短的和长的电脉冲(称为点和划)组成。点和划的时间长度都有规定,以一点为一个基本单位,一划等于三个点的长度,正好对应电报的“滴”和“答”。国际摩尔斯电码如下图所示。


就像电报只能发出“滴”和“答”声一样,计算机只认识0和1两种字符,但是,人类的文字是多种多样的,如何把人类的文字转换成计算机认识的0、1字符呢?这个过程同样需要字符编码。

字符编码(Character Encoding)是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。

和摩尔斯电码的功能类似,20世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系做了统一的规定,这套编码被称为 ASCII 码,一直沿用至今。

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套计算机编码系统。它主要用于显示现代英语,其中共有128个字符。

标准ASCII码也叫作基础ASCII码,使用7位二进制数( 剩下的1位二进制数为0)来表示所有的大写和小写字母、数字0到9、标点符号,以及在美式英语中使用的特殊控制字符。

由于ASCII码只有128个字符,虽然可以表示所有的英文字符,但世界上还有很多其他的文字,ASCII码是无法表示的,所以需要一种更加全面的字符编码。

在介绍其他的字符编码之前,我们先来说一下一个计算机领域通用的字符集。


2.Unicode

Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域中的一项业界标准。它对世界上大部分的文字系统进行了整理和编码,使得计算机可以用更简单的方式来呈现和处理文字。

Unicode至今仍在不断增修,每个新版本都加入了更多新的字符。目前最新的版本为2019年5月公布的v12.1,这一版本只新增了一个字符,即日本新年号的合字(上令下和)。

Unicode备受认可,并广泛地应用于计算机软件的国际化与本地化过程。有很多新科技,如可扩展置标语言(Extensible Markup Language,简称XML)、Java编程语言及现代的操作系统都采用了Unicode编码。

Unicode是一套通用的字符集,包含世界上的大部分文字,也就是说,Unicode是可以表示中文的。


有了 Unicode 为什么还需要 UTF-8
Unicode虽然统一了全世界字符的编码,但没有规定如何存储。这么做有如下考虑:
如果Unicode统一规定,那么每个符号要用三个或四个字节表示,因为字符太多,所以只能用这么多字节才能表示完全。
一旦这么规定,那么每个英文字母前都必然有2~3字节是0,因为所有英文字母在ASCII码中都有,都可以用一个字节表示,剩余字节位置就要补充0。
如果这样,那么文本文件的大小会因此大出二三倍,这对于存储来说是极大的浪费。为了解决这个问题,就出现了一些中间格式的字符集,它们被称为通用转换格式,即UTF(Unicode Transformation Format)。常见的UTF格式有UTF-7、UTF-7.5、UTF-8、UTF-16和UTF-32。
● UTF-8 使用 1 ~ 4 字节为每个字符编码。
● UTF-16 使用 2 或 4 字节为每个字符编码。
● UTF-32 使用 4 字节为每个字符编码。
所以我们可以说,UTF-8、UTF-16等都是Unicode的一种实现方式。
举个例子,Unicode规定了一个中文字符“我”对应的Unicode编码是“\u6211”,但是,在UTF-8和UTF-16等不同的实现方式下,这个二进制code的存储方式是不一样的。
UTF-8使用可变长度字节来储存 Unicode字符,例如ASCII字母继续使用1字节储存,重音文字、希腊字母或西里尔字母等使用2字节来储存,而常用的汉字就要使用3字节,辅助平面字符则使用4字节。


有了 UTF-8 为什么还需要 GBK


因为UTF-8是Unicode的一种实现,所以它包含了世界上的所有文字的编码,UTF-8采用1~4字节进行编码。

对于那些排在前面优先纳入的字符,可能就优先使用1字节、2字节存储了,对于后纳入的文字符,就要使用3字节或者4字节存储了。

那些晚一些纳入的字符,在UTF-8中的存储所占的字节数可能就会多一些,对存储空间的要求就会很大。

对于常用的汉字,在UTF-8中采用3字节进行编码,如果有一种只包含中文和ASCII的编码,那么就不需要使用3字节存储了,可能2字节就够了。

对于大部分只服务一个国家或者地区的网站来说,比如一个中国的网站,一般会出现简体字和繁体字及一些英文字符,很少出现日语或者韩文。

也是出于这样的考虑,原国家标准总局于1981年制定并实施了GB 2312-80编码,即中华人民共和国国家标准简体中文字符集。后来微软利用GB 2312-80未使用的编码空间,收录GB 13000.1-93全部字符制定了GBK编码。

有了标准中文字符集,如果是一个纯中文网站,那么就可以采用这种编码方式,可以大大节省一些存储空间。

常用的中文编码有GBK、GB2312、GB18030等,最常用的是GBK。 

GB2312(1980 年):16 位字符集,收录了 6763 个简体汉字和 682 个符号,共 7445个字符。

◎ 优点:适用于简体中文环境,属于中国国家标准,通行于中国大陆地区,新加坡也使用此编码。

◎ 缺点:不兼容繁体中文,其汉字集合过少。

GBK(1995 年):16 位字符集,收录了 21003 个汉字和 883 个符号,共 21886 个字符。

◎ 优点:适用于简繁中文共存的环境,为简体 Windows 所使用,向下完全兼容GB2312,向上支持 ISO-10646 国际标准;所有字符都可以一对一映射到 Unicode 2.0上。

◎ 缺点:不属于官方标准,很多搜索引擎都不能很好地支持 GBK 汉字。

GB18030(2000 年):32 位字符集,收录了 27484 个汉字,同时收录了藏文、蒙文、维吾尔文等主要的少数民族文字。

◎ 优点:可以收录所有可能想到的文字和符号,属于最新的国家标准。

◎ 缺点:目前支持它的软件较少。


为什么会存在乱码


我们还以前面介绍的发电报为例,假设有以下场景:

发报员使用“美式摩尔斯电码”将情报转换成电报,收报员收到电报之后,通过“现代国际摩尔斯电码”进行破译,那么得到的情报内容可能完全看不懂,这就是乱码了。

就像在计算机领域,我们把一串中文字符通过UTF-8进行编码传输给别人,别人得到这串文字之后,通过GBK进行解码,得到的内容可能是“锟届瀿锟斤拷雮傡锟斤拷直锟斤拷锟”,这就是乱码。

比如以下代码:

public static void main(String[] args) throws UnsupportedEncodingException { String s = " 我爱中国!"; byte[] bytes = s.getBytes(Charset.forName("GBK")); System.out.println("GBK 编码,GBK 解码:" + new String(bytes, "GBK")); System.out.println("GBK 编码,GB18030 解码:" + new String(bytes, "GB18030")); System.out.println("GBK 编码,UTF-8 解码:" + new String(bytes, "UTF-8"));}


输出结果如下:

GBK 编码,GBK 解码:我爱中国 !GBK 编码,GB18030 解码:我爱中国 !GBK 编码,UTF-8 解码:�Ұ��й�!

可以看到,将中文字符通过GBK编码,再使用UTF-8解码,得到的字符就是一串问号,这就是乱码了。

因为Unicode是一直在更新的,在这个过程中,肯定有一些比较新的字符是无法表示的。即使Unicode发布了新版纳入了某个文字,但很多软件系统并未升级也会出现这样的问题。

就像一些手机厂商新出的那些emoji表情,在自己的手机上可以正常显示,发送到其他品牌的手机上可能就无法显示。这其实也是字符集不支持导致的。

当无法显示正常字符或者表情时需要有一个字符来表示,在Unicode中,这个字符就是�,它也是Unicode中定义的一个特殊字符。也就是“0xFFFD REPLACEMENT CHARACTER”,所有无法表示的字符都会通过这个字符来表示。

Unicode官方有关于这个符号的介绍,它的10进制值是65533,在UTF-8下,它的十六进制形式是“0xEF 0xBF 0xBD”(三个字节)。

如果有两个连续的字符都无法显示,如“��”,那么在UTF-8编码下,十六进制值为:

0xEF 0xBF 0xBD 0xEF 0xBF 0xBD


以上这段编码,如果放到GBK中进行解码,因为GBK中一个汉字占用2字节,那么结果如下:

0xEF 0xBF, 0xBD 0xEF, 0xBF 0xBD

即:

0xEFBF0xBDEF0xBFBD

如果展示出来,就是锟(0xEFBF)、斤(0xBDEF)、拷(0xBFBD)。所以,以后再见到锟斤拷,第一时间就要想到UTF-8和GBK的转换问题。

除了锟斤拷,还有两组比较经典的乱码,分别是“烫烫烫”和“屯屯屯”,这两组乱码产生自Visual C,这是Debug模式下Visual C对内存的初始化操作。Visual C会把栈中新分配的内存初始化为0xcc,而把堆中新分配的内存初始化为0xcd。把0xcc和0xcd按照字符打印出来,就是烫和屯了。


本文节选自《深入理解Java核心技术:写给Java工程师的干货笔记(基础篇)》一书,欢迎阅读本书夯实你的Java技术!





写了啥

本书是《Java 工程师成神之路》系列丛书的第一本,所谓"基础不牢,地动山摇",这本书就是来给大家筑基的!

之前经常有人问,到底达到一个什么样的水品,可以称之为高级工程师?会哪些东西,才能叫作Java技术专家?

抛开那些高大上的框架、中间件、分布式什么的理论,这里把所有作者认为一个Java开发者需要掌握的基础知识全部都放到这本书里面了。

全书一共有23个章节,比较丰富全面的覆盖到了Java开发者日常工作中用到的几乎所有基础知识。

《道德经》里面的一句话"有道无术,术尚可求;有术无道,则止于术",那到底什么是"道"什么是"术"呢?

在写这本书之前作者也一直在探索,但是在写完之后,把这本书全部通读一遍之后,作者豁然开悟。

原来书中提出的那些问题,那些问题背后的思考,以及思考背后的原理解析,就是作者一直所追寻的"道"。

所以,作者又改了第二版、第三版、第四版....不断地删除了原来的一些"概念"、"用法",不断地新增进去更多的"原理"和"思考",不断地尝试着通过各种通俗的语言、例子把这些道理讲清楚。



适合谁

但是,虽然作者穷尽了各种办法,想让书中的内容通俗易懂,但还是想说:这本书并不是适合所有人的。

以下几类人群不适合读这本书:

1、完全没有编程经验的人;这本书不是cook book,不能帮你21天掌握Java,并没有从安装虚拟机、配置classpath讲起。

2、认为代码写出来能跑就行,抵制一切"八股"的人;这本书中很多的知识点都是来源于工作中遇到的"坑"、书中分析了很多源码,是一本深入原理的书籍。一本十足的"八股"宝典。

其他人,无论是刚工作一两年的新手,还是工作了五六年的老司机;无论是准备找工作需要一本面试宝典,还是需要一本书来帮你躲避工作中的那些坑;无论是体系化学习作为知识体系,还是日常学习作为查缺补漏。

这本书,都适合你



本书特色

所谓一图胜千言,为了方便大家理解,这本书中画了很多图

为了给大家带来更好的阅读体验,这本书采取的是彩印版。全书500多页,全部都是彩印。

为了方便读者阅读和理解,除了前两章,本书尽量摒弃了太过枯燥的概念性描述,也避免堆砌大篇幅的代码,试图通过举例、比喻、引用等方式把Java体系中的很多原理知识讲解清楚。

因为Java体系中的很多知识点都是相关联的,所以本书在提到其他知识点时,为了方便读者进行关联性学习,会标注相关章节和内容提要。

本书更加注重对原理的解读,很多语法的概念介绍及使用方式并不是本书的重点。所以,本书中大部分内容均是Java开发者需要重点关注的一些知识点,很多知识点的总结均来源于日常开发中遇到的对各种线上问题的排查与总结。



怎么买

现在书籍已经在京东、当当等平台开启预售了。现在京东、当当自营的价格是7折的。

但是,对待自己的亲粉丝,怎么能没有专属优惠呢?

给大家申请了专享6折的优惠,但是是限时限量的,所以,欲购从速喽。扫描下方二维码,就可以直达优惠链接了。

长按扫码立享优惠

另外,随书附送的还有一个史无前例的小惊喜给到大家,看到之后会让你忍不住惊呼的那种!

还等啥,上车吧!

 

如果喜欢本文
欢迎 在看留言分享至朋友圈 三连

▼点击阅读原文,了解本书详情~

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存