1. 程式人生 > 其它 >c中如何將多個位元組的16進位制數合併成一個_一次搞懂Java中的編碼問題

c中如何將多個位元組的16進位制數合併成一個_一次搞懂Java中的編碼問題

技術標籤:c中如何將多個位元組的16進位制數合併成一個

編碼問題一直是一個困擾程式設計師的問題,尤其是對於java程式設計師。因為java的跨平臺特性,經常需要在多個編碼之間進行轉換。

下面詳細講一講java中的編碼問題

一、為什麼要編碼

長話短說,原因如下:

1,計算機儲存資訊的最小單位是位元組Byte。佔8個二進位制位。所以一個位元組能表示的狀態只有255中

2,人類語言的符號太多,255位不夠

3,所以必須把多個位元組合起來表示一個人類語言符號

4,在怎麼組合位元組上出現了多種方法,這就是編碼

二、常用編碼格式

現在的編碼格式有很多,常見的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。

能表示漢字的有GB2312、GBK、UTF-8、UTF-16 這幾種。不同的編碼格式在儲存空間和編碼效率方面都有不同。

如何選擇,需要多方考慮。

先簡單介紹一下這幾種編碼。

ASCII 碼

最基礎的編碼格式,標準ASCII碼只有127個。連一個位元組都沒用完,只用了低7位。

後來又除了擴充套件ASCII碼把第8位利用了起來。不過總的來說,能表示的字元非常有限。

僅適用於英語系語種

屬於單子節編碼格式。

ISO-8859-1

這個就是上面說的擴充套件ASCII碼。向下相容標準ASCII碼

雖然只能表示256個字元,但是已經包含了絕大多數西歐語言,所以使用上還是非常廣泛的。

GB2312

全稱是《資訊交換用漢字編碼字符集 基本集》,請注意這個基本字眼。它是雙位元組編碼,總的編碼範圍是 A1-F7,其中從 A1-A9 是符號區,總共包含 682 個符號,從 B0-F7 是漢字區,包含 6763 個漢字。

GBK

全稱叫《漢字內碼擴充套件規範》。另一個名字叫擴充套件GB2312。所以你就知道了,GBK是GB2312的擴充套件,能表示 21003 個漢字。向下相容GB2312,所以用GB2312編碼的漢字用GBK來解碼是沒有問題的。

GBK是雙位元組的,不論編碼英文字母還是漢字都是雙位元組的。

GB18030(相容GB2312)

全稱是《資訊交換用漢字編碼字符集》,是我國的強制標準,它是可變位元組的,可能是單位元組、雙位元組或者四位元組編碼,它的編碼與 GB2312 編碼相容,這個雖然是國家標準,但是實際系統中使用的並不廣泛。

Unicode 編碼方案

看了這麼多編碼,很煩對吧。所以國際標準化組織ISO制定了Unicode 編碼方案,可以容納世界上所有文字和符號的字元編碼。

這裡要多說一句,Unicode僅僅是一個字符集,規定了一個人類字元對應的二進位制數。至於這個二進位制數的儲存方案,是不管的,由其他開發者實現。這個組織只出標準,不出實現。

所以流行的Unicode 編碼方案有兩種UTF-16和UTF-8。其實還有一個UTF-32,用的少。。

UTF-32

32很好理解,就是32位,定長4個位元組。不管什麼字元,統統32位。

好處好壞處很明顯。效率高(不需要做任何編碼計算轉換),但是浪費空間。空間換時間。

UTF-8

這個8不是8位的意思,而是最少8位。UTF-8是一種變長的編碼方式。使用 1~6 個位元組來儲存;

對於英語等西歐語系,非常喜歡UTF-8。因為他們只需要一個位元組即可,

在單子節的情況下和 ASCII 編碼完全一樣,因此 UTF-8 是相容 ASCII 的

但是對於中日韓語系來說,就比較難受,不僅要忍受效率的損失,而且儲存空間也沒有節省

UTF-16

UTF-16是介於兩者之間的一個編碼格式。算是一種折中。折中編碼會固定2位元組或者4位元組

具體來說是對於 Unicode 編號範圍在 0000 ~ FFFF 之間的字元,UTF-16 使用兩個位元組儲存,並且直接儲存 Unicode 編號,不用進行編碼轉換,這跟 UTF-32 非常類似。

對於 Unicode 編號範圍在 10000~10FFFF 之間的字元,UTF-16 使用四個位元組儲存

注意,只有 UTF-8 相容 ASCII,UTF-32 和 UTF-16 都不相容 ASCII,因為它們沒有單位元組編碼。

所以 UTF-8 和 UTF-16/32 都各有優缺點,因此選擇的時候應當立足於實際的應用場景。實際上,我們在普通的應用開發中,基本不太關係這個。99%的場景是直接使用了UTF-8。我認為這並沒有多大問題,UTF-8的變長特性,決定了以後即使繼續增加人類語言字元,utf-8都可以放下,反觀utf-16/32可能會有問題。

Java中的編解碼

編碼解碼主要發生在字元和位元組的轉換過程中,這個場景一般就是IO,包括磁碟IO和網路IO

io中的編解碼

解碼就是由位元組到字元的轉換,如從網路上拿來的二進位制流,轉換成漢字。

b4701ad082a9acefe28d230a07f57042.png

Reader 類是 Java 的 I/O 中讀字元的父類,而 InputStream 類是讀位元組的父類,InputStreamReader 類就是關聯位元組到字元的橋樑,它負責在 I/O 過程中處理讀取位元組到字元的轉換,而具體位元組到字元的解碼實現它由 StreamDecoder 去實現,在 StreamDecoder 解碼過程中必須由使用者指定 Charset 編碼格式。值得注意的是如果你沒有指定 Charset,將使用本地環境中的預設字符集,例如在中文環境中將使用 GBK 編碼。

寫的情況也是類似,寫字元的父類是 Writer,寫位元組的父類是 OutputStream,通過 OutputStreamWriter 轉換字元到位元組。如下圖所示:

5137133b1fcfd928e404ff2268a25097.png

同樣 StreamEncoder 類負責將字元編碼成位元組,編碼格式和預設編碼規則與解碼是一致的。

寫個程式碼測試一下:

public class IOTest { @Test public void testWrite() throws Exception { String file = "d:/test1.txt"; String charset = "UTF-8"; // 寫字元換轉成位元組流 FileOutputStream outputStream = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter( outputStream, charset); try { writer.write("儲存點中文字元"); } finally { writer.close(); } } @Test public void testRead() throws Exception { String file = "d:/test1.txt"; String charset = "UTF-8"; // 讀取位元組轉換成字元 FileInputStream inputStream = new FileInputStream(file); InputStreamReader reader = new InputStreamReader( inputStream, charset); StringBuffer buffer = new StringBuffer(); char[] buf = new char[64]; int count = 0; try { while ((count = reader.read(buf)) != -1) { buffer.append(buf, 0, count); } } finally { reader.close(); } System.out.println(buffer.toString()); }}

你可以改下字符集,多測幾次。讀寫使用不同的字符集會亂碼。

我們在寫程式碼的時候,一定要手動指定下字符集,不要使用預設。雖然在一臺機器上,不會出問題。一旦涉及到跨機器可能有異常。

我們可以看下預設的字符集到底是啥。

這是jdk的原始碼。在vm啟動時,找有沒有配file.encoding引數,配了,去找對應的字符集。如果字符集比較特殊,在不同的機器上有可能找不到的。否則就是UTF-8

/**

* Returns the default charset of this Java virtual machine.

*

*

The default charset is determined during virtual-machine startup and

* typically depends upon the locale and charset of the underlying

* operating system.

*

* @return A charset object for the default charset

*

* @since 1.5

*/

public static Charset defaultCharset() {

if (defaultCharset == null) {

synchronized (Charset.class) {

String csn = AccessController.doPrivileged(

new GetPropertyAction("file.encoding"));

Charset cs = lookup(csn);

if (cs != null)

defaultCharset = cs;

else

defaultCharset = forName("UTF-8");

}

}

return defaultCharset;

}

java程式碼中的編解碼

除了io之外,我們也經常在程式碼中進行string和byte的轉換。

String s = "中文字串";

byte[] b = s.getBytes("UTF-8");

String n = new String(b,"UTF-8");

隨手指定字符集是個好習慣