編碼的幾種實現

幾個概念:

Unicode是一種“編碼”,所謂編碼就是一個編號(數字)到字符的一種映射關係,就僅僅是一種一對一的映射而已。

Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。

 

 GBK、UTF-8是一種“編碼格式”,是用來存儲或序列化編碼的一種“格式”。

 

UTF-8是對Unicode的一種存儲實現。

 

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

 

漢字"光"

Unicode編碼:
01010001  01001001
即:
char c1='光';
char c2='\u5149'; (\u表示Unicode)
char c3= (char)20809;
以上三種是等價的。

UTF-8編碼格式:
11100101  10000101  10001001
即:E5 85 89

 

 

URLEncoder編碼原理:

1、將需要轉換的內容轉換成"%xy"的形式,xy是原字節拆分成高低4位後補0後的兩個16進制的數值。

2、空格轉化爲加號(" "轉換爲"+")。

3、不需要轉換的內容:字符"a"-"z","A"-"Z","0"-"9",".","-","*",和"_" 都不會被編碼。

 

例子:

"a"編碼:
01100001

URLEncoder:
a


"光"UTF-8編碼:
11100101 10000101 10001001

URLEncoder(UTF-8):
%E5%85%89

URLEncoder(GBK):
%B9%E2



 

 

Base64編碼原理:

3個字符4個字符,不足3個用"="填充。

這4個字符前兩位爲00,8*3=6*4。

 

例子:

"a"編碼:
01100001

base64:
YQ==
00011000 00010000 = =


"光"UTF-8編碼:
11100101 10000101 10001001

base64:
5YWJ
00111001 00011000 00010110 00001001


Hex編碼原理:

每個字符按高低4位轉換爲兩個字符。

 

例子:

"a"編碼:
01100001

Hex:
61
00000110 00000001


"光"UTF-8編碼:
11100101 10000101 10001001

Hex:
e58589
00001110 00000101 00001000 00000101 00001000 00001001


 

 

測試代碼:

    @Test
    public void test40() {
        try {
            String str = "光";

            for (char c : str.toCharArray()) {
                System.out.print(c);
            }
            System.out.println();

            String a = URLEncoder.encode(str, "UTF-8");
            System.out.println(a.length());
            System.out.println(a);
            String a1 = URLEncoder.encode(str, "GBK");
            System.out.println(a1.length());
            System.out.println(a1);

            String b = TextCodec.BASE64.encode(str);
            System.out.println(b.length());
            System.out.println(b);

            char[] c = Hex.encode(str.getBytes());
            System.out.println(c.length);
            System.out.println(c);

            byte[] decode = Hex.decode(new String(c));
            System.out.println(decode.length);
            System.out.println(new String(decode));

            char d = '\u5149';
            System.out.println(d);


            char c1 = str.charAt(0);
            int c2 = c1 - 0;
            System.out.println(c2);
            System.out.println((char)c2);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test41() {
        String str = "01010001 01001001";
        int number = getNumber(str);
        System.out.println(number);
    }

    public int getNumber(String str) {
        int ans = 0;
        int index = 0;
        for (int i = str.length() - 1; i >= 0; i--) {
            if (str.charAt(i) == '1') {
                ans += Math.pow(2, index);
            } else if (str.charAt(i) == ' ') {
                continue;
            }
            index++;
        }
        return ans;
    }

一些常見的編碼問題:

1、 Little endian和Big endian

上一節已經提到,Unicode碼可以採用UCS-2格式直接存儲。以漢字"嚴"爲例,Unicode碼是4E25,需要用兩個字節存儲,一個字節是4E,另一個字節是25。存儲的時候,4E在前,25在後,就是Big endian方式;25在前,4E在後,就是Little endian方式。

這兩個古怪的名稱來自英國作家斯威夫特的《格列佛遊記》。在該書中,小人國裏爆發了內戰,戰爭起因是人們爭論,喫雞蛋時究竟是從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開。爲了這件事情,前後爆發了六次戰爭,一個皇帝送了命,另一個皇帝丟了王位。

因此,第一個字節在前,就是"大頭方式"(Big endian),第二個字節在前就是"小頭方式"(Little endian)。

那麼很自然的,就會出現一個問題:計算機怎麼知道某一個文件到底採用哪一種方式編碼?

Unicode規範中定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做"零寬度非換行空格"(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節,而且FF比FE大1。

如果一個文本文件的頭兩個字節是FE FF,就表示該文件採用大頭方式;如果頭兩個字節是FF FE,就表示該文件採用小頭方式。

2、通過BOM(byte-order mark)頭區分UTF8、UTF16LE、UTF16BE編碼

編碼 BOM
UTF8

EF BB BF

UTF16BE

FE FF 

UTF16LE FF FE

UTF-8一般無需BOM頭。

3、ANSI編碼

在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文操作系統下,ANSI 編碼代表 JIS 編碼。

4、GBK是中國製定的一個編碼標準

GBK編碼佔兩個字節。

5、ASCII,GB2312,GBK和GB18030編碼關係?全角與半角?

兼容性關係是GB18030兼容GBK,GBK兼容GB2312,GB2312兼容ASCII。

所謂兼容,你可以簡單理解爲子集、不衝突的關係。例如GB2312編碼的文件中可以出現ASCII字符,GBK編碼的文件中可以出現GB2312和ASCII字符,GB18030編碼的文件可以出現GBK、GB2312、ASCII字符。

GB2312兼容ASCII的同時對ASCII中的符號做了重新編碼,比如一個數字2,ASCII對應的二進制是0x32,而GB2312對應的二進制是 0xA3 0xB2;GBK2312的2是全角的2,ASCII的2是半角的2,輸入法裏的切換全角半角。

 

 

參考文章:

https://www.cnblogs.com/gavin-num1/p/5170247.html

https://www.cnblogs.com/zj0208/p/7019102.html

https://www.cnblogs.com/xqxacm/p/4886718.html

https://www.cnblogs.com/xqxacm/p/4888062.html

https://www.cnblogs.com/xqxacm/p/4886299.html

http://blog.sina.com.cn/s/blog_66dc3ab4010163u5.html

https://www.cnblogs.com/tarol/p/7523642.html

https://cloud.tencent.com/developer/article/1343240

https://blog.csdn.net/qq_38880380/article/details/78445628

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章