日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unicode、UTF-8、UTF-16

發布時間:2023/12/10 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unicode、UTF-8、UTF-16 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

計算機起源于美國,上個世紀,他們對英語字符與二進制位之間的關系做了統一規定,并制定了一套字符編碼規則,這套編碼規則被稱為ASCII編碼

ASCII 編碼一共定義了128個字符的編碼規則,用七位二進制表示 ( 0x00 - 0x7F ), 這些字符組成的集合就叫做 ASCII 字符集

隨著計算機的普及,在不同的地區和國家又出現了很多字符編碼,比如: 大陸的 GB2312、港臺的 BIG5, 日本的 Shift JIS等等

由于字符編碼不同,計算機在不同國家之間的交流變得很困難,經常會出現亂碼的問題,比如:對于同一個二進制數據,不同的編碼會解析出不同的字符

當互聯網迅猛發展,地域限制打破之后,人們迫切的希望有一種統一的規則, 對所有國家和地區的字符進行編碼,于是 Unicode 就出現了

Unicode 簡介

Unicode 是國際標準字符集,它將世界各種語言的每個字符定義一個唯一的編碼,以滿足跨語言、跨平臺的文本信息轉換

Unicode 字符集的編碼范圍是?0x0000 - 0x10FFFF?, 可以容納一百多萬個字符, 每個字符都有一個獨一無二的編碼,也即每個字符都有一個二進制數值和它對應,這里的二進制數值也叫?碼點?, 比如:漢字?"中"?的 碼點是?0x4E2D, 大寫字母?A?的碼點是?0x41, 具體字符對應的 Unicode 編碼可以查詢?Unicode字符編碼表

字符集和字符編碼

字符集是很多個字符的集合,例如 GB2312 是簡體中文的字符集,它收錄了六千多個常用的簡體漢字及一些符號,數字,拼音等字符

字符編碼是 字符集的一種實現方式,把字符集中的字符映射為特定的字節或字節序列,它是一種規則

比如:Unicode 只是字符集,UTF-8、UTF-16、UTF-32 才是真正的字符編碼規則

Unicode 字符存儲

Unicode 是一個符號集, 它只規定了每個符號的二進制值,但是符號具體如何存儲它并沒有規定

前面提到, Unicode 字符集的編碼范圍是?0x0000 - 0x10FFFF,因此需要 1 到 3 個字節來表示

那么,對于三個字節的 Unicode字符,計算機怎么知道它表示的是一個字符而不是三個字符呢 ?

如果所有字符都用三個字節表示,那么對于那些一個字節就能表示的字符來說,有兩個字節是無意義的,對于存儲來說,這是極大的浪費,假如 , 一個普通的文本, 大部分字符都只需一個字節就能表示,現在如果需要三個字節才能表示,文本的大小會大出三倍左右

因此,Unicode 出現了多種存儲方式,常見的有 UTF-8、UTF-16、UTF-32,它們分別用不同的二進制格式來表示 Unicode 字符

UTF-8、UTF-16、UTF-32 中的 "UTF" 是 "Unicode Transformation Format" 的縮寫,意思是"Unicode 轉換格式",后面的數 字表明至少使用多少個比特位來存儲字符, 比如:UTF-8 最少需要8個比特位也就是一個字節來存儲,對應的, UTF-16 和 UTF-32 分別需要最少 2 個字節 和 4 個字節來存儲

UTF-8 編碼

UTF-8: 是一種變長字符編碼,被定義為將碼點編碼為 1 至 4 個字節,具體取決于碼點數值中有效二進制位的數量

UTF-8 的編碼規則:

  • 對于單字節的符號,字節的第一位設為?0,后面 7 位為這個符號的 Unicode 碼。因此對于英語字母,UTF-8 編碼和 ASCII 碼是相同的, 所以 UTF-8 能兼容 ASCII 編碼,這也是互聯網普遍采用 UTF-8 的原因之一

  • 對于?n?字節的符號(?n > 1),第一個字節的前?n?位都設為?1,第?n + 1?位設為?0,后面字節的前兩位一律設為?10?。剩下的沒有提及的二進制位,全部為這個符號的 Unicode 碼

  • 下表是Unicode編碼對應UTF-8需要的字節數量以及編碼格式

    Unicode編碼范圍(16進制)UTF-8編碼方式(二進制)
    000000 - 00007F0xxxxxxx?ASCII碼
    000080 - 0007FF110xxxxx?10xxxxxx
    000800 - 00FFFF1110xxxx?10xxxxxx?10xxxxxx
    01 0000 - 10 FFFF11110xxx?10xxxxxx?10xxxxxx?10xxxxxx

    表格中第一列是Unicode編碼的范圍,第二列是對應UTF-8編碼方式,其中紅色的二進制?"1"?和?"0"?是固定的前綴, 字母?x?表示可用編碼的二進制位

    根據上面表格,要解析 UTF-8 編碼就很簡單了,如果一個字節第一位是?0?,則這個字節就是一個單獨的字符,如果第一位是?1?,則連續有多少個?1?,就表示當前字符占用多少個字節

    下面以?"中"?字 為例來說明 UTF-8 的編碼,具體的步驟如下圖, 為了便于說明,圖中左邊加了?1,2,3,4?的步驟編號

    首先查詢?"中"?字的 Unicode 碼?0x4E2D, 轉成二進制, 總共有 16 個二進制位, 具體如上圖 步驟1 所示

    通過前面的 Unicode 編碼和 UTF-8 編碼的表格知道,Unicode 碼?0x4E2D?對應?000800 - 00FFFF?的范圍,所以,?"中"?字的 UTF-8 編碼 需要?3?個字節,即格式是?1110xxxx?10xxxxxx?10xxxxxx

    然后從?"中"?字的最后一個二進制位開始,按照從后向前的順序依次填入格式中的?x?字符,多出的二進制補為?0, 具體如上圖 步驟2、步驟3 所示

    于是,就得到了?"中"?的 UTF-8 編碼是?11100100?10111000?10101101, 轉換成十六進制就是?0xE4B8AD, 具體如上圖 步驟4 所示

    UTF-16 編碼

    UTF-16 也是一種變長字符編碼, 這種編碼方式比較特殊, 它將字符編碼成 2 字節 或者 4 字節

    具體的編碼規則如下:

  • 對于 Unicode 碼小于?0x10000?的字符, 使用?2?個字節存儲,并且是直接存儲 Unicode 碼,不用進行編碼轉換

  • 對于 Unicode 碼在?0x10000?和?0x10FFFF?之間的字符,使用?4?個字節存儲,這?4?個字節分成前后兩部分,每個部分各兩個字節,其中,前面兩個字節的前?6?位二進制固定為?110110,后面兩個字節的前 6 位二進制固定為?110111, 前后部分各剩余 10 位二進制表示符號的 Unicode 碼 減去?0x10000?的結果

  • 大于?0x10FFFF?的 Unicode 碼無法用 UTF-16 編碼

  • 下表是Unicode編碼對應UTF-16編碼格式

    Unicode編碼范圍(16進制)具體Unicode碼(二進制)UTF-16編碼方式(二進制)字節
    0000 0000 - 0000 FFFFxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx2
    0001 0000 - 0010 FFFFyy yyyyyyyy xx xxxxxxxx110110yy yyyyyyyy?110111xx xxxxxxxx4

    表格中第一列是Unicode編碼的范圍,第二列是 具體Unicode碼的二進制 ( 第二行的第二列表示的是 Unicode 碼 減去?0x10000?后的二進制 ) , 第三列是對應UTF-16編碼方式,其中紅色的二進制?"1"?和?"0"?是固定的前綴, 字母?x?和?y?表示可用編碼的二進制位, 第四列表示 編碼占用的字節數

    前面提到過,"中"?字的 Unicode 碼是?4E2D, 它小于?0x10000,根據表格可知,它的 UTF-16 編碼占兩個字節,并且和 Unicode 碼相同,所以?"中"?字的 UTF-16 編碼為?4E2D

    我從?Unicode字符表網站?找了一個老的南阿拉伯字母, 它的 Unicode 碼是:?0x10A6F?, 可以訪問?https://unicode-table.com/cn/10A6F/?查看字符的說明, Unicode 碼對應的字符如下圖所示

    下面以這個 老的南阿拉伯字母的 Unicode 碼?0x10A6F?為例來說明 UTF-16?4?字節的編碼,具體步驟如下,為了便于說明,圖中左邊加了?1,2,3,4 、5的步驟編號

    首先把 Unicode 碼?0x10A6F?轉成二進制, 對應上圖的 步驟 1

    然后把 Unicode 碼?0x10A6F?減去?0x10000, 結果為?0xA6F?并把這個值轉成二進制?00 00000010?10 01101111,對應上圖的 步驟 2

    然后 從二進制?00 00000010?10 01101111?的最后一個二進制為開始,按照從后向前的順序依次填入格式中的?x?和?y?字符,多出的二進制補為?0, 對應上圖的 步驟 3、 步驟 4

    于是,就計算出了 Unicode 碼?0x10A6F?的 UTF-16 編碼是?11011000 00000010?11011110 01101111?, 轉換成十六進制就是?0xD802DE6F, 對應上圖的 步驟 5

    UTF-32 編碼

    UTF-32 是固定長度的編碼,始終占用 4 個字節,足以容納所有的 Unicode 字符,所以直接存儲 Unicode 碼即可,不需要任何編碼轉換。雖然浪費了空間,但提高了效率。

    UTF-8、UTF-16、UTF-32 之間如何轉換

    前面介紹過,UTF-8、UTF-16、UTF-32 是 Unicode 碼表示成不同的二進制格式的編碼規則,同樣,通過這三種編碼的二進制表示,也能獲得對應的 Unicode 碼,有了字符的 Unicode 碼,按照上面介紹的 UTF-8、UTF-16、UTF-32 的編碼方法 就能轉換成任一種編碼了

    UTF 字節序

    最小編碼單元是多字節才會有字節序的問題存在,UTF-8 最小編碼單元是一字節,所以 它是沒有字節序的問題,UTF-16 最小編碼單元是 2 個字節,在解析一個 UTF-16 字符之前,需要知道每個編碼單元的字節序

    比如:前面提到過,"中"?字的 Unicode 碼是?4E2D,?"?"?字符的 Unicode 碼是?2D4E, 當我們收到一個 UTF-16 字節流?4E2D?時,計算機如何識別它表示的是字符?"中"?還是 字符?"?"?呢 ?

    所以,對于多字節的編碼單元,需要有一個標記顯式的告訴計算機,按照什么樣的順序解析字符,也就是字節序,字節序分為 大端字節序 和 小端字節序

    小端字節序簡寫為 LE( Little-Endian ), 表示 低位字節在前,高位字節在后, 高位字節保存在內存的高地址端,而低位字節保存在內存的低地址端

    大端字節序簡寫為 BE( Big-Endian ), 表示 高位字節在前,低位字節在后,高位字節保存在內存的低地址端,低位字節保存在在內存的高地址端

    下面以?0x4E2D?為例來說明大端和小端,具體參見下圖:

    數據是從高位字節到低位字節顯示的,這也更符合人們閱讀數據的習慣,而內存地址是從低地址向高地址增加

    所以,字符0x4E2D?數據的高位字節是?4E,低位字節是?2D

    按照大端字節序的高位字節保存內存低地址端的規則,4E?保存到低內存地址?0x10001?上,2D?則保存到高內存地址?0x10002?上

    對于小端字節序,則正好相反,數據的高位字節保存到內存的高地址端,低位字節保存到內存低地址端的,所以?4E?保存到高內存地址?0x10002?上,2D?則保存到低內存地址?0x10001?上

    BOM

    BOM 是 byte-order mark 的縮寫,是 "字節序標記" 的意思, 它常被用來當做標識文件是以 UTF-8、UTF-16 或 UTF-32 編碼的標記

    在 Unicode 編碼中有一個叫做 "零寬度非換行空格" 的字符 ( ZERO WIDTH NO-BREAK SPACE ), 用字符?FEFF?來表示

    對于 UTF-16 ,如果接收到以?FEFF?開頭的字節流, 就表明是大端字節序,如果接收到?FFFE, 就表明字節流 是小端字節序

    UTF-8 沒有字節序問題,上述字符只是用來標識它是 UTF-8 文件,而不是用來說明字節順序的。"零寬度非換行空格" 字符 的 UTF-8 編碼是?EF BB BF, 所以如果接收到以?EF BB BF?開頭的字節流,就知道這是UTF-8 文件

    下面的表格列出了不同 UTF 格式的固定文件頭

    UTF編碼固定文件頭
    UTF-8EF BB BF
    UTF-16LEFF FE
    UTF-16BEFE FF
    UTF-32LEFF FE 00 00
    UTF-32BE00 00 FE FF

    根據上面的 固定文件頭,下面列出了?"中"?字在文件中的存儲 ( 包含文件頭 )

    編碼固定文件頭
    Unicode 編碼0X004E2D
    UTF-8EF BB BF?4E 2D
    UTF-16BEFE FF?4E 2D
    UTF-16LEFF FE?2D 4E
    UTF-32BE00 00 FE FF?00 00 4E 2D
    UTF-32LEFF FE 00 00?2D 4E 00 00

    常見的字符編碼的問題

    • Redis 中文key的顯示

    有時候我們需要向redis中寫入含有中文的數據,然后在查看數據,但是會看到一些其他的字符,而不是我們寫入的中文

    上圖中,我們向redis 寫入了一個 "中" 字,通過 get 命令查看的時候無法顯示我們寫入的 "中" 字

    這時候加一個 --raw 參數,重新啟動 redis-cli 即可,也即 執行 redis-cli --raw 命令啟動redis客戶端,具體的如下圖所示

    • MySQL 中的 utf8 和 utf8mb4

    MySQL 中的 "utf8" 實際上不是真正的 UTF-8, "utf8" 只支持每個字符最多 3 個字節, 對于超過 3 個字節的字符就會出錯, 而真正的 UTF-8 至少要支持 4 個字節

    MySQL 中的 "utf8mb4" 才是真正的 UTF-8


    下面以 test 表為例來說明, 表結構如下:

    mysql> show create table test\G *************************** 1. row ***************************Table: test Create Table: CREATE TABLE `test` (`name` char(32) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec)

    向?test?表分別插入?"中"?字 和 Unicode 碼為?0x10A6F?的字符,這個字符需要從?https://unicode-table.com/cn/10A6F/?直接復制到 MySQL 控制臺上,手工輸入會無效,具體的執行結果如下圖:

    從上圖可以看出,插入?"中"?字 成功,插入?0x10A6F?字符失敗,錯誤提示無效的字符串,\xF0\X90\XA9\xAF?正是?0x10A6F?字符的 UTF-8 編碼,占用?4?個字節, 因為 MySQL 的 utf8 編碼最多只支持?3?個字節,所以插入會失敗

    把?test?表的字符集改成?utf8mb4?, 排序規則 改成?utf8bm4_unicode_ci, 具體如下圖所示:

    字符集和排序方式修改之后,再次插入?0x10A6F?字符, 結果是成功的,具體執行結果如下圖所示

    上圖中,set names utf8mb4?是為了測試方便,臨時修改當前會話的字符集,以便保持和 服務器一致,實際解決這個問題需要修改?my.cnf?配置中 服務器和客戶端的字符集

    小結

    本文從字符編碼的歷史介紹了 Unicode 出現的原因,接著介紹了 Unicode 字符集中 三種不同的編碼方式:UTF-8、UTF-16、UTF-32 以及它們的的編碼方法,緊接著介紹了 字節序、BOM ,最后講到了字符集在 MySQL 和 Redis 應用中常見的問題以及解決方案 ,更多關于 Unicode 的介紹請參考 Unicode 的 RFC 文檔

    總結

    以上是生活随笔為你收集整理的Unicode、UTF-8、UTF-16的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。