10.23 從 String.getBytes 理解 Java 編碼和解碼

原碼,補碼,反碼

因為原碼,補碼,反碼比較簡單,我這裡粘貼一個例子進行展示。

從 String.getBytes 理解 Java 編碼和解碼

Unicode 和 UTF-8 的關係

Uincode 是一個字符集。它規定了我們使用到的字或符號的碼點(code point)。碼點使用 16 進制保存。

從 String.getBytes 理解 Java 編碼和解碼

Uincode 字符集規定 一 的碼點為 4E00。

Uincode 字符集規定 丁 的碼點為 4E01。

計算機呢只能識別二進制的 0 和 1。而 UTF-8 指的是編碼規則,規定碼點怎麼保存成二進制。

還有別的 Unicode 編碼規則,UTF-16 和 UTF-32。

從 String.getBytes 理解 Java 編碼和解碼

上述表格簡單描述了Unicode 按 UTF-8 編碼的格式。

  • 首先將 16 進制的碼點,通過進制轉換 為十進制
  • 然後使用十進制的數字查找上述表格處於哪個範圍中,得出編碼規則。
  • 然後將碼點轉換為 2 進制,從低位到高位替換 x 即可得到字二進制的原碼
  • 將二進制的原碼轉換為補碼存儲。

java 內存中的字符串採用的是 unicode 編碼,也就是內編碼。我們可以從 unicode 轉變為 GBK 或 UTF-8 等其它規則。

代碼驗證猜想

以趙為例子講解。

趙的碼點為:8D75

16 進制的碼點轉換為 10 進制:36213

36213 處於 2048-65535 ,得出對應的 UTF-8 編碼格式為:1110xxxx 10xxxxxx 10xxxxxx

趙的 16 進制碼點 8D75 轉換為二進制 1000

將二進制填充在 1110xxxx 10xxxxxx 10xxxxxx 中的 x 中,不足的補 0.

11101000 10110101 10110101。

對三個字節分別求補碼為:

原碼:11101000 10110101 10110101

補碼:10011000 11001011 11001011

補碼對應 java 中的字節數組為:{-24,-75,-75}

@Test
public void run454() throws UnsupportedEncodingException {
String str ="趙";
final byte[] bytes = str.getBytes("UTF-8");
StringBuilder stringBuilder =new StringBuilder();
for (byte aByte : bytes) {
stringBuilder.append(aByte).append(",");
}
System.out.println(stringBuilder.toString());
}
  • 再加一個例子:

且的碼點:4E14

16 進制的碼點轉換為 10 進制:19988

19988 處於 2048-65535 ,得出對應的 UTF-8 編碼格式為:1110xxxx 10xxxxxx 10xxxxxx

16 進制的碼點轉換成二進制:100111000010100

原碼:11100100 10111000 10010100

補碼:10011100 11001000 11101100

補碼對應的字節數組為:{-28,-72,-108}

@Test
public void run43() throws UnsupportedEncodingException {
// {-28,-72,-108}
String str ="且";
final byte[] bytes = str.getBytes("UTF-8");

StringBuilder stringBuilder =new StringBuilder();
for (byte aByte : bytes) {
stringBuilder.append(aByte).append(",");
}
System.out.println(stringBuilder.toString());
}

GBK 轉碼

趙的 GBK 碼點為:D5D4 十六進制碼點轉換為二進制:11010101 11010100 源碼:11010101 11010100 補碼:10101011 10101100

補碼對應的字節數組為:{-43,-44}

@Test
public void run454() throws UnsupportedEncodingException {
String str ="趙";
final byte[] bytes = str.getBytes("GBK");
StringBuilder stringBuilder =new StringBuilder();
for (byte aByte : bytes) {
stringBuilder.append(aByte).append(",");
}
// -43,-44
System.out.println(stringBuilder.toString());
}

JAVA 中亂碼問題

java 字符或字符串採用 uincode 作為內編碼。

@Test
public void run44() {
String str="\\\\u0c2c";
// బ
System.out.println(str);
// ✈
System.out.println("\\\\u2708");
}

編碼:字符串到字節。

解碼:字節到字符串。

當我們讀取文件的時候實際讀取的是字節。然後根據文件的編碼格式,將字節解碼成字符串。亂碼問題容易出現的地方就是這裡。

不要妄想將一個亂碼的字符串變成一個非亂碼的。這個思路是錯誤的。應該從亂碼之前的字節著手處理。

@Test
public void run100() throws UnsupportedEncodingException {
String str = "張";
final byte[] gbks = str.getBytes("GBK");
final String s = new String(gbks, "UTF-8");
System.out.println(s);
}

上述例子中的 s 已經亂碼了,當你操作這個 s 獲取字節也是亂碼的。

因此思路是操作 gbks 轉換解碼方式獲取字符串。

最後,我自己是一名從事了多年開發的JAVA老程序員,今年年初我花了一個月整理了一份最適合2019年學習的java學習乾貨,可以送給每一位喜歡java的小夥伴,想要獲取的可以關注我的頭條號並在後臺私信我:[交流]即可免費獲取。


從 String.getBytes 理解 Java 編碼和解碼


分享到:


相關文章: