【JAVA编码专题】总结
第一部分:編碼基礎
為什么需要編碼:用計算機看得懂的語言(二進制數)表示各種各樣的字符。
一、基本概念
ASCII、Unicode、big5、GBK等為字符集,它們只定義了這個字符集內有哪些字符,以及分別用什么數字表示。
而UTF-8與UTF-16則定義了Unicode字符集如何使用計算機看得懂的語言進行傳輸和保存。
例如: Unicode 字符 U+00A9 = 1010 1001 (版權符號) 在 UTF-8 里的編碼為:
?????? 11000010 10101001 = 0xC2 0xA9
事實上,沒有必要將它們嚴格區分,一些字符集本身就是編碼方式,如ASCII。它們均表示如何用二進制數表示一個字符。
因此很多地方將UTF-8、UTF-16與ASCII、GBK等統一當作字符編碼方式。
二、常見編碼
明白了各種語言需要交流,經過翻譯是必要的,那又如何來翻譯呢?計算中提拱了多種翻譯方式,常見的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。它們都可以被看作為字典,它們規定了轉化的規則,按照這個規則就可以讓計算機正確的表示我們的字符。目前的編碼格式很多,例如 GB2312、GBK、UTF-8、UTF-16 這幾種格式都可以表示一個漢字,那我們到底選擇哪種編碼格式來存儲漢字呢?這就要考慮到其它因素了,是存儲空間重要還是編碼的效率重要。根據這些因素來正確選擇編碼格式,下面簡要介紹一下這幾種編碼格式。
ASCII 碼
學過計算機的人都知道 ASCII 碼,總共有 128 個,用一個字節的低 7 位表示,0~31 是控制字符如換行回車刪除等;32~126 是打印字符,可以通過鍵盤輸入并且能夠顯示出來。
ISO-8859-1
128 個字符顯然是不夠用的,于是 ISO 組織在 ASCII 碼基礎上又制定了一些列標準用來擴展 ASCII 編碼,它們是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵蓋了大多數西歐語言字符,所有應用的最廣泛。ISO-8859-1 仍然是單字節編碼,它總共能表示 256 個字符。
GB2312
它的全稱是《信息交換用漢字編碼字符集 基本集》,它是雙字節編碼,總的編碼范圍是 A1-F7,其中從 A1-A9 是符號區,總共包含 682 個符號,從 B0-F7 是漢字區,包含 6763 個漢字。
GBK
全稱叫《漢字內碼擴展規范》,是國家技術監督局為 windows95 所制定的新的漢字內碼規范,它的出現是為了擴展 GB2312,加入更多的漢字,它的編碼范圍是 8140~FEFE(去掉 XX7F)總共有 23940 個碼位,它能表示 21003 個漢字,它的編碼是和 GB2312 兼容的,也就是說用 GB2312 編碼的漢字可以用 GBK 來解碼,并且不會有亂碼。
GB18030
全稱是《信息交換用漢字編碼字符集》,是我國的強制標準,它可能是單字節、雙字節或者四字節編碼,它的編碼與 GB2312 編碼兼容,這個雖然是國家標準,但是實際應用系統中使用的并不廣泛。
UTF-16
說到 UTF 必須要提到 Unicode(Universal Code 統一碼),ISO 試圖想創建一個全新的超語言字典,世界上所有的語言都可以通過這本字典來相互翻譯。可想而知這個字典是多么的復雜,關于 Unicode 的詳細規范可以參考相應文檔。Unicode 是 Java 和 XML 的基礎,下面詳細介紹 Unicode 在計算機中的存儲形式。
UTF-16 具體定義了 Unicode 字符在計算機中存取方法。UTF-16 用兩個字節來表示 Unicode 轉化格式,這個是定長的表示方法,不論什么字符都可以用兩個字節表示,兩個字節是 16 個 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每兩個字節表示一個字符,這個在字符串操作時就大大簡化了操作,這也是 Java 以 UTF-16 作為內存的字符存儲格式的一個很重要的原因。
UTF-8
UTF16固定使用2個字節(或者4個字節)來表示字符,這導致與早期大量使用的ASCII碼無法兼容,同時一些特殊字符在UNIX系統中存在特殊含義,如 '/0' 或 '/', 它們在 文件名和其他 C 庫函數參數里都有特別的含義。此外,一些最常用的字符(西歐字符)只用一個字節就可以表示,若使用UTF16則浪費帶寬或者存儲空間。
三、 UTF-8詳細介紹
首先 UCS 和 Unicode 只是分配整數給字符的編碼表. 現在存在好幾種將一串字符表示為一串字節的方法. 最顯而易見的兩種方法是將 Unicode 文本存儲為 2 個 或 4 個字節序列的串. 這兩種方法的正式名稱分別為 UCS-2 和 UCS-4. 除非另外指定, 否則大多數的字節都是這樣的(Bigendian convention). 將一個 ASCII 或 Latin-1 的文件轉換成 UCS-2 只需簡單地在每個 ASCII 字節前插入 0x00. 如果要轉換成 UCS-4, 則必須在每個 ASCII 字節前插入三個 0x00.
在 Unix 下使用 UCS-2 (或 UCS-4) 會導致非常嚴重的問題. 用這些編碼的字符串會包含一些特殊的字符, 比如 '/0' 或 '/', 它們在 文件名和其他 C 庫函數參數里都有特別的含義. 另外, 大多數使用 ASCII 文件的 UNIX 下的工具, 如果不進行重大修改是無法讀取 16 位的字符的. 基于這些原因, 在文件名, 文本文件, 環境變量等地方, UCS-2 不適合作為 Unicode 的外部編碼.
在 ISO 10646-1 Annex R 和 RFC 2279 里定義的 UTF-8 編碼沒有這些問題. 它是在 Unix 風格的操作系統下使用 Unicode 的明顯的方法.
UTF-8 有一下特性:
UCS 字符 U+0000 到 U+007F (ASCII) 被編碼為字節 0x00 到 0x7F (ASCII 兼容). 這意味著只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 兩種編碼方式下是一樣的.
所有 >U+007F 的 UCS 字符被編碼為一個多個字節的串, 每個字節都有標記位集. 因此, ASCII 字節 (0x00-0x7F) 不可能作為任何其他字符的一部分.
表示非 ASCII 字符的多字節串的第一個字節總是在 0xC0 到 0xFD 的范圍里, 并指出這個字符包含多少個字節. 多字節串的其余字節都在 0x80 到 0xBF 范圍里. 這使得重新同步非常容易, 并使編碼無國界, 且很少受丟失字節的影響.
可以編入所有可能的 231個 UCS 代碼
UTF-8 編碼字符理論上可以最多到 6 個字節長, 然而 16 位 BMP 字符最多只用到 3 字節長.
Bigendian UCS-4 字節串的排列順序是預定的.
字節 0xFE 和 0xFF 在 UTF-8 編碼中從未用到.
下列字節串用來表示一個字符. 用到哪個串取決于該字符在 Unicode 中的序號.
U-00000000 - U-0000007F:?? ?0xxxxxxx
U-00000080 - U-000007FF:?? ?110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF:?? ?1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF:?? ?11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF:?? ?111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF:?? ?1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
xxx 的位置由字符編碼數的二進制表示的位填入. 越靠右的 x 具有越少的特殊意義. 只用最短的那個足夠表達一個字符編碼數的多字節串. 注意在多字節串中, 第一個字節的開頭"1"的數目就是整個串中字節的數目.
例如: Unicode 字符 U+00A9 = 1010 1001 (版權符號) 在 UTF-8 里的編碼為:
11000010 10101001 = 0xC2 0xA9
而字符 U+2260 = 0010 0010 0110 0000 (不等于) 編碼為:
11100010 10001001 10100000 = 0xE2 0x89 0xA0
這種編碼的官方名字拼寫為 UTF-8, 其中 UTF 代表 UCS Transformation Format. 請勿在任何文檔中用其他名字 (比如 utf8 或 UTF_8) 來表示 UTF-8, 當然除非你指的是一個變量名而不是這種編碼本身.
第二部分:JAVA編碼
一、String中的編碼
1、JVM的默認編碼為UTF-8
2、獲取String的編碼
可以通過public byte[] getBytes(String charsetName) throws UnsupportedEncodingException獲取某個String的編碼
615ae4b8ade69687
615ae4b8ade69687
615ae4b8ade69687
feff0061005a4e2d6587
feff0061005a4e2d6587
615ad6d0cec4
由此可以看出,unicode與utf16可以認為是同一方式。
3、通過byte[]編碼構建String
public String(byte[] bytes, String charsetName) throws UnsupportedEncodingException
用什么方式生成的編碼,就應該用原有的方式進行還原
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
若用其它編碼格式來構建String,則出現亂碼
輸出:
慚?隇
慚?隇
aZ涓枃
附完整代碼及輸出
package org.ljh.javademo.encode;import java.io.IOException; import java.nio.charset.Charset;public class DefaultEncode {public static void main(String[] args) throws IOException {// 一、獲取當前JVM環境的默認編碼System.out.println(Charset.defaultCharset());// 二、獲取String在各種編碼格式中的編碼String s = "aZ中文";// 1、默認的Unicode編碼byte[] bytesDefault = s.getBytes();System.out.println(getHexString(bytesDefault));// 2、也是默認的編碼byte[] bytesDefault2 = s.getBytes(Charset.defaultCharset());System.out.println(getHexString(bytesDefault2));// 3、UTF-8編碼byte[] bytesUTF8 = s.getBytes("UTF-8");System.out.println(getHexString(bytesUTF8));// 4、UTF-16編碼byte[] bytesUTF16 = s.getBytes("UTF-16");System.out.println(getHexString(bytesUTF16));// 5、unicode編碼byte[] bytesUnicode = s.getBytes("UNICODE");System.out.println(getHexString(bytesUnicode));// 6、GBK編碼byte[] bytesGBK = s.getBytes("GBK");System.out.println(getHexString(bytesGBK));// 三、通過byte[]編碼構建String,用什么方式生成的編碼,就應該用原有的方式進行還原// 1、默認編碼String sDefault = new String(bytesDefault);System.out.println(sDefault);// 2、默認編碼2String sDefault2 = new String(bytesDefault2, Charset.defaultCharset());System.out.println(sDefault2);// 3、UTF-8編碼String sUTF8 = new String(bytesUTF8, "UTF8");System.out.println(sUTF8);// 4、UTF-16編碼String sUTF16 = new String(bytesUTF16, "UTF16");System.out.println(sUTF16);// 5、unicode編碼String sUnicode = new String(bytesUnicode, "Unicode");System.out.println(sUnicode);// 6、GBK編碼String sGBK = new String(bytesGBK, "GBK");System.out.println(sGBK);// 若通過其它編碼方式進行還原,則出現亂碼// 1、用UTF-16編碼解碼UTF8生成的字節String sUTF16Erro = new String(bytesDefault, "UTF16");System.out.println(sUTF16Erro);// 2、用unicode編碼解碼UTF8生成的字節String sUnicodeError = new String(bytesDefault, "Unicode");System.out.println(sUnicodeError);// 3、用GBK編碼解碼UTF8生成的字節String sGBKError = new String(bytesDefault, "GBK");System.out.println(sGBKError);}// 輸入byte[],將之轉化為16進制的字符進行輸出,如輸入{90,20,21},則返回5A1415。因為默認情況下byte以10進制格式進行輸出private static String getHexString(byte[] b) {String hexs = "";for (int i = 0; i < b.length; i++) {String hex = Integer.toHexString(b[i] & 0xFF);if (hex.length() == 1) {hex = '0' + hex;}hexs += hex;}return hexs;}}輸出:
UTF-8
615ae4b8ade69687
615ae4b8ade69687
615ae4b8ade69687
feff0061005a4e2d6587
feff0061005a4e2d6587
615ad6d0cec4
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
慚?隇
慚?隇
aZ涓枃
二、JAVA IO中的編碼
總結
以上是生活随笔為你收集整理的【JAVA编码专题】总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【JAVA编码专题】深入分析 Java
- 下一篇: Avro基础