赞
踩
❝我们平时在处理文本文件或者网络请求时,时不时会遇到乱码的情况,这篇文章就带你彻底搞懂编码和乱码
❞
首先,我们要知道,在计算机中,一切都是用0和1来表示的。普通的txt文件、或者客户端发过来的数据等等,这些一切其实都是通过0和1转化而来的。「那它是怎样从0和1转化我们人能看懂的字母或汉字呢?」
起初,计算机是由美国人发明的,而且那时候基本就在美国运行,因此,开始只考虑到了美国的需求,0和1只要能转化成英文字母和符号就行了,大概需要128个字符,所以就规定了0和1转化为这128个字符的规则,称作「ASCII编码」。
计算机存储的最小单位是byte,而1byte有8个bit位,足够表示128个字符(「第一位用0表示,剩下7位从0-127分别表示128个字符」),因此ASCII编码就规定1byte对应一个英文字符,对应关系如下图:
虽然ASCII编码对于美国来说足够用了,但是其他国家显然是不够的,于是,各个国家就发明了自己的编码方式,比如我们「中国的GBK和GB2312等」。但是各个国家自己的编码也都会兼容ASCII编码(毕竟人家是鼻祖),前面说到ASCII编码的第一位为0,则其他国家自己的编码都会定义第一位为1,这样就能区分当前是ASCII编码还是自己国家的编码。
对于美国而言,一个字节就能表示所有的字符,但是在中国就不行了,光常见的汉字就有好几千个了,还不包括符号,因此,中国首先出现的编码形式是「GB2312」,它使用两个字节来表示,这种编码主要表示的是简体中文,不包括繁体字。
后来在GB2312的基础上,又发明了「GBK」编码(「GB2312表示的字符,用GBK显示完全一样」),增加了一万多个汉字,里面包括繁体字,这就是我们日常使用最常见的国内编码。此编码也是使用两个字节来表示。
在这里提一下,怎么看一个字符使用了几个字节,可以使用文本编辑器,我这里使用的是EmEditor,当然,其他的也行,如果是nodepad++则需要装一个插件查看二进制。
打开EmEditor,双击右下角的编码,选择GB2312
接着输入一个字符,然后再双击右下角的编码,选择二进制(十六进制)视图。
可以看到,使用的是两个字节。这里再提一点,「为啥查看二进制,很多编辑器默认都是显示的是十六进制」?为啥不直接显示二进制,这是因为,一个字节有8个bit位,而一个十六进制数字能表示4个bit位,所以一个字节刚好可以使用两个十六进制来表示,就像上面显示的“B9 FA",两个表示一个字节,既简单又直观,如果直接使用二进制,要显示十六个0或1,眼都要看花掉。
因此,查看字符的二进制数据,最好是通过十六进制来查看。
世界上有很多国家,每个国家都有自己特有的编码方式,这就导致了编码不统一,容易造成乱码的情况。那有什么方式可以让编码统一起来呢?可以通过「Unicode」。
Unicode和其他编码方式不一样,它没有规定字符对应的二进制,也没有规定每个字符占多少个字节,「它只做了一件事,就是给世界上所有的字符分配了一个唯一的数字编号」,就像人的身份证一样,这是唯一的,它的范围从0x000000到0x10FFFF之间(十六进制以0x开头,二进制以0b开头,八进制以0开头),这个编号一般写成十六进制,前面再加上\u。例如”国“的Unicode就是”\u56fd“。
那这些编号怎么对应到二进制呢?可以采用UTF-8或者UTF-16和UTF-32等。我们平时使用的最多的就是「UTF-8」。
UTF-8使用的字节跟Unicode编号的大小有关,编号越大,使用的字节越多,字节个数在1-4之间。对于大部分汉字而已,一个中文字符需要三个字节。而且UTF-8兼容ASCII。
我们平时出现乱码主要有三种情况。
「第一种」:用文本编辑器打开一个文本文件时,显示乱码。
这种情况是因为,我们平时有时候文本编辑器设置了固定UTF-8编码,但是突然打开一个GBK编码个文件,这时候就会出现乱码,里面只有汉字会乱码。
解决方法:只要将编辑器的编码方式设置成对应的即可。
「第二种」:在程序中读取文件,出现乱码。
这种情况是因为,在程序中读取文件一般都会设置编码,有时候省略编码就会读取系统的默认编码,如果设置的编码和文件的编码对应不上就会出现乱码。
解决方法:读取文件时设置的编码和文件编码保持一致。
「第三种」:接收请求数据出现乱码。
网络中传输的数据也都是二进制数据,所以在我们传输数据的时候,发送方指定一种编码将数据转成二进制通过网络传输到接收方,接收方再指定一种编码将二进制数据转成字符数据。如果发送方和接收方指定的编码不一致,则会出现乱码情况。
解决方法:发送方和接收方采用统一编码即可,一般都是采用UTF-8。
❝注意:不论是字符串转二进制,还是二进制转字符串都需要指定编码,例如Java中字符串的getBytes()方法将字符串转成字节数组,虽然方法可以不传编码格式,但内部会传入系统默认编码Charset.defaultCharset().name()。又比如String(byte bytes[], String charsetName)方法将字节数组转成字符串,也依然需要传入编码格式,如果不传就会采用系统默认编码。
❞
Java面试系列-线程相关(一)
2020-09-03
Java到底是引用传递还是值传递
2020-08-07
事务:不好意思,你被隔离了!
2020-07-23
spring事务咋和新冠病毒一样,还会传染?
2020-07-05
数据是怎么一步一步到服务器的
2020-06-18
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。