2019-12-22

ASCII,GB2312,GBK,Unicode,UTF-8字符编码到底是什么?

  • 谈到这个问题就不得不说在编码过程中遇到的乱码问题,我想这应该是每一个程序员都头疼的问题。
    首先,我们需要思考:
    一. 什么是字符?

字符指类字形单位或符号,包括字母、数字、运算符号、标点符号和其他符号,通常由8个二进制位(一个字节)来表示一个字符。 字符是计算机中经常用到的二进制编码形式,也是计算机中最常用到的信息形式。

二.什么是字符集?

字符集简单来说就是字符的集合,它的种类比较多,每种字符集包含的字符个数不同,常见字符集有:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。

三.什么是字符编码?

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

  • 好了,了解这些基础知识后,我们可以进一步了解常见的字符编码

ASCII

  • 我们应该清楚电脑中所有数据其实都是二进制数据,电脑真正能识别的只有0和1,1byte = 8bit,因此总共有2^7=128种表示方式,包括32个字符,其余96位为英文字母和标点符号及运算符号等,其二进制表示范围为00000000-01111111。


    ASCII字符集

GB2312

  • GB2312 是最早一版的中文编码,每个字占据2bytes。由于要和ASCII兼容,所以2b字节最高位不可以为0了(否则和ASCII会有冲突)。在GB2312中收录了6763个汉字以及682个特殊符号,已经囊括了生活中最常用的所有汉字。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”,“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768,其中有5个空位是D7FA-D7FE。(摘自百度百科,了解即可)

GBK

-GBK编码,是在GB2312标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312标准

Uncoide

  • Unicode是计算机科学领域里的一项业界标准,包括字符集、编码方案等,它可以容纳世界上所有文字和符号,Unicode为世界上所有字符都分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF(十六进制),有110多万,每个字符都有一个唯一的Unicode编号,这个编号一般写成16进制,在前面加上U+。具体的符号对硬表可以查看官网。

UTF-8

  • UTF-8可以说是我们最常用的编码格式了,之前的Uncoide功能确实强大,包含了世界的所有文字和编号,但它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储,直到互联网的普及催生了UTF-8编码方式。
    UTF-8 的编码规则很简单,只有二条:
    1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

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

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
  • 跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
    列:
public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "汉";
        byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
        for (byte b:bytes){
            System.out.println("该字节的十进制数str是 : " + b);
            System.out.println("该字节的2进制数str是 : "+ (Integer.toBinaryString(b)+""));
            System.out.println("该字节的16进制数是 : U+"+ (Integer.toHexString(b)+""));
        }
    }

结果:

该字节的十进制数str是 : -26
该字节的2进制数str是 : 11111111111111111111111111100110
该字节的16进制数是 : U+ffffffe6
该字节的十进制数str是 : -79
该字节的2进制数str是 : 11111111111111111111111110110001
该字节的16进制数是 : U+ffffffb1
该字节的十进制数str是 : -119
该字节的2进制数str是 : 11111111111111111111111110001001
该字节的16进制数是 : U+ffffff89

由此可见汉是由三个字节组成的,其二进制是11100110 10110001 10001001,转换成16进制为e6b189;

  • 针对对常见乱码问题怎样解决?
    乱码一般都是string字符串转换错误引起的,我们需要首先了解下string常用的两个编码转换方式getBytes(Charset charset)和new String(byte[] bytes,Charset charset)
/**
     * Encodes this {@code String} into a sequence of bytes using the given
     * {@linkplain java.nio.charset.Charset charset}, storing the result into a
     * new byte array.
     *
     * 

This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement byte array. The * {@link java.nio.charset.CharsetEncoder} class should be used when more * control over the encoding process is required. * * @param charset * The {@linkplain java.nio.charset.Charset} to be used to encode * the {@code String} * * @return The resultant byte array * * @since 1.6 */ public byte[] getBytes(Charset charset) { if (charset == null) throw new NullPointerException(); return StringCoding.encode(charset, value, 0, value.length); } /** * Constructs a new {@code String} by decoding the specified array of * bytes using the specified {@linkplain java.nio.charset.Charset charset}. * The length of the new {@code String} is a function of the charset, and * hence may not be equal to the length of the byte array. * *

This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement string. The {@link * java.nio.charset.CharsetDecoder} class should be used when more control * over the decoding process is required. * * @param bytes * The bytes to be decoded into characters * * @param charset * The {@linkplain java.nio.charset.Charset charset} to be used to * decode the {@code bytes} * * @since 1.6 */ public String(byte bytes[], Charset charset) { this(bytes, 0, bytes.length, charset); }

  • getBytes(Charset charset)方法表明的很清楚,用给定的字符编码生成字节数组,并将结果放入新数组里,简单来说就是把我们认识的字变成一连串不认识的编码符;new String(byte[] bytes,Charset charset)通过给定字符编码解码数组并构造新的字符串,简单来说就是把我们不认识的字符变成我们熟悉的字。因此,常见解决办法主要有以下几种:
1.
String  msg =  request.getParameter("message");
String  str =  new String(msgs.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8)
2.
request.setCharacterEncoding("UTF-8");
3.
  在你的web.xml里加入如下几行:  
    
  CharacterEncoding   
  filters.SetCharacterEncodingFilter   
     
  encoding   
  UTF-8   
     
     
         
  CharacterEncoding   
  /*   
     
4.
 打开tomcat的server.xml文件,加入如下一行:URIEncoding=“UTF-8"
  完整的应如下:   
        

原创不易, 如需转载,请注明出处!
——纯生啤酒_

你可能感兴趣的:(2019-12-22)