你了解Java中的幾種編碼方式?解決亂碼問題可能并不麻煩
前言
編碼的轉(zhuǎn)換通常在IO機制中使用,一個好的編碼可以為我們節(jié)省很多空間,在某種程度上提高我們應(yīng)用的效率。由于之前就知道String中的轉(zhuǎn)換方式,還有一些工具類,因此今天就好好的整理一下java中jdk提供的幾種轉(zhuǎn)換方式,希望對你有幫助。
一、編碼轉(zhuǎn)換原理
1、為什么需要編碼
我們知道計算機存儲信息的最小單位是一個字節(jié)8位,能夠表示256個字符。這對于早起的英文來說足夠了。即使是加上一些常見符號也足夠。于是在1965年美國制定了ASCII編碼,主要用于英語和西歐語言,一開始128個,后來加到了256。
后來隨著時間的發(fā)展,中國、日本等國的計算機也開始蓬勃發(fā)展,于是計算機不僅僅要存儲英文了,也開始存儲中文。但是中文我們都知道幾萬個太多了,一個字節(jié)肯定放不下。怎么辦呢?一個字節(jié)表示不下,那就多用幾個字節(jié)就好了。這樣不僅可以表示漢字,還可以避免了與ASCII編碼的沖突。這幾個字節(jié)在存儲的時候再轉(zhuǎn)化為bit,完美!劃重點哈,編碼解決的就是字節(jié)和字符之間的轉(zhuǎn)化問題。
2、編碼方式
既然是編碼,那些大佬早就考慮到了這些問題,并提拱了多種編碼方式,常見的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。它們規(guī)定了轉(zhuǎn)化的規(guī)則,按照這個規(guī)則就可以讓計算機正確的表示我們的字符。
像GB2312、GBK、UTF-8、UTF-16等很多種方式都可以表示漢字,他們有什么區(qū)別呢?
(1)GB2312
它是雙字節(jié)編碼,總的編碼范圍是 A1-F7,其中從 A1-A9 是符號區(qū),總共包含 682 個符號,從 B0-F7 是漢字區(qū),包含 6763 個漢字。這個是中國1981年搞出來的。這種編碼是一個漢字兩個字節(jié)。
(2)GBK
它的編碼范圍是 8140~FEFE(去掉 XX7F)總共有 23940 個碼位,它能表示 21003 個漢字。這個是中國在1995年搞出來的,主要是用于GB2312編碼的補充。這種編碼依然是一個漢字兩個字節(jié)。
(3)Unicode
上面看到,中國可以做出了一個編碼,日本也可以做出來一個編碼,時間久了每個國家都有著自己的一套編碼,就不可避免的造成沖突。于是Unicode出來了,把所有語言統(tǒng)一起來合成一個規(guī)則。這種編碼是定長的字節(jié)數(shù)。
(4)UTF8
既然Unicode是定長的字節(jié)數(shù),那么存儲一個復(fù)雜的漢字可能需要三個字節(jié),但是為了保證是2的冪數(shù)集,就會自動擴充為4個字節(jié),別看著一個字節(jié)之差,存儲的字?jǐn)?shù)多了就會極大的浪費空間。是于是而 UTF-8 采用了一種變長技術(shù),每個編碼區(qū)域有不同的字碼長度。不同類型的字符可以是由 1~6 個字節(jié)組成。
以上這些編碼方式會為每一個漢字或者是字母建立一個編碼庫,在編碼的時候字母和編碼一一對應(yīng)。
3、為什么會出現(xiàn)亂碼?
這個問題就是因為編碼和解碼是采用了不同的或者是不兼容的編碼方案。比如一個用UTF-8編碼的后的字符。再用GBK去解碼,由于兩個字符集的編碼庫不一樣。同一個漢字在兩個編碼庫的位置也不一樣。于是就出現(xiàn)了亂碼。
4、java如何解決亂碼問題?
這個問題其實就是java中如何使用編碼規(guī)則,因為使用好了編碼規(guī)則。才可以很好地解決亂碼問題。
(1)IO流
編碼的目的上面已經(jīng)說了,主要是字節(jié)和字符之間的轉(zhuǎn)化。既然涉及到字節(jié)和字符很容易我們就能想到j(luò)ava中的IO流。也就是說java中編碼的轉(zhuǎn)換其實就是IO流中的類來實現(xiàn)的。
最核心的就是上面幾個類,當(dāng)然這里只是給出了輸入的一部分,還有一些輸出的類。
(2)String
String類中也提供了一些轉(zhuǎn)碼的方法。下面我們會通過實例來說明。為什么String可以實現(xiàn)呢?這是因為String底層保存的其實就是一個一個字節(jié),而且String還有方法直接轉(zhuǎn)化為字符。所以String肯定也能實現(xiàn)。
(3)Charset
這個Charset是javaNIO中的一個類,整個流程就是讀取數(shù)據(jù),然后轉(zhuǎn)化為byte,也就是字符。然后重新編碼成字符就OK了。
下面我們使用代碼來實現(xiàn)一下:
二、代碼實現(xiàn)
1、IO流
首先是IO流實現(xiàn),這種通過輸入輸出流可以直接的指定編碼規(guī)則。
- public void convertionFile() throws IOException {
- File file = new File("./愚公要移山.txt");
- FileInputStream fis = new FileInputStream(file);
- InputStreamReader inReader = new InputStreamReader(fis, "gbk");
- FileOutputStream fos = new FileOutputStream(file);
- OutputStreamWriter outReader = new OutputStreamWriter(fos, "utf-8");
- //這種輸入gbk,輸出utf-8肯定會出現(xiàn)錯誤
- }
2、String
使用string是最方便的,代碼也比較簡潔,適用于字符串的編碼。
- public void convertionString() throws UnsupportedEncodingException {
- String s = "愚公要移山,碼農(nóng)飛上天";
- // 正常情況下轉(zhuǎn)碼的過程
- byte[] b = s.getBytes("gbk");// 編碼
- String sa = new String(b, "gbk");// 解碼
- System.out.println(sa);
- // 錯誤狀態(tài)下轉(zhuǎn)碼的過程
- b = sa.getBytes("utf-8");// 編碼使用utf-8
- sa = new String(b, "gbk");// 解碼使用gbk
- System.err.println(sa);
- }
- //控制臺輸出:
- //愚公要移山,碼農(nóng)飛上天
- //鎰氬叕瑕佺Щ灞憋紝鐮佸啘椋炰笂澶?
3、Charset
- public void convertionCharset() throws IOException {
- Charset charset = StandardCharsets.UTF_8;
- // 從字符集中創(chuàng)建相應(yīng)的編碼和解碼器
- CharsetEncoder encoder = charset.newEncoder();
- CharsetDecoder decoder = charset.newDecoder();
- // 構(gòu)造一個buffer
- CharBuffer charBuffer = CharBuffer.allocate(64);
- charBuffer.put('A');
- charBuffer.flip();
- // 將字符序列轉(zhuǎn)換成字節(jié)序列
- ByteBuffer bb = encoder.encode(charBuffer);
- // 將字節(jié)序列轉(zhuǎn)換成字符序列
- bb.flip();
- CharBuffer cb = decoder.decode(bb);
- }
以上就是三種基本的實現(xiàn)方式,當(dāng)然還有一些其他的,比如Spring中提供的編碼轉(zhuǎn)換工具等等。在這里就不說了,因為技術(shù)太多,實現(xiàn)的方式也太多,我們就看這幾種即可。
本文轉(zhuǎn)載自微信公眾號「愚公要移山」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系愚公要移山公眾號。