java中文亂碼解決之道(五)—–java是如何編碼解碼的

編碼&解碼

1:I/O操作

2:內存

3:數據庫

4:javaWeb

下面主要介紹前面兩種場景,數據庫部分只要設置正確編碼格式就不會有什么問題,javaWeb場景過多需要了解URL、get、POST的編碼,servlet的解碼,所以javaWeb場景下節LZ介紹。

I/O操作
在前面LZ就提過亂碼問題無非就是轉碼過程中編碼格式的不統一產生的,比如編碼時采用UTF-8,解碼采用GBK,但最根本的原因是字符到字節或者字節到字符的轉換出問題了,而這中情況的轉換最主要的場景就是I/O操作的時候。當然I/O操作主要包括網絡I/O(也就是javaWeb)和磁盤I/O。網絡I/O下節介紹。

首先我們先看I/O的編碼操作。



InputStream為字節輸入流的所有類的超類,Reader為讀取字符流的抽象類。java讀取文件的方式分為按字節流讀取和按字符流讀取,其中InputStream、Reader是這兩種讀取方式的超類。

按字節

我們一般都是使用InputStream.read()方法在數據流中讀取字節(read()每次都只讀取一個字節,效率非常慢,我們一般都是使用read(byte[])),然后保存在一個byte[]數組中,最后轉換為String。在我們讀取文件時,讀取字節的編碼取決于文件所使用的編碼格式,而在轉換為String過程中也會涉及到編碼的問題,如果兩者之間的編碼格式不同可能會出現問題。例如存在一個問題test.txt編碼格式為UTF-8,那么通過字節流讀取文件時所獲得的數據流編碼格式就是UTF-8,而我們在轉化成String過程中如果不指定編碼格式,則默認使用系統編碼格式(GBK)來解碼操作,由于兩者編碼格式不一致,那么在構造String過程肯定會產生亂碼,如下:

File file = new File("C:\\test.txt");InputStream input = new FileInputStream(file);StringBuffer buffer = new StringBuffer();byte[] bytes = new byte[1024];for(int n ; (n = input.read(bytes))!=-1 ; ){buffer.append(new String(bytes,0,n));}System.out.println(buffer);

輸出結果:锘挎垜鏄?cm

test.txt中的內容為:我是 cm。

要想不出現亂碼,在構造String過程中指定編碼格式,使得編碼解碼時兩者編碼格式保持一致即可:

buffer.append(new String(bytes,0,n,"UTF-8"));

按字符

其實字符流可以看做是一種包裝流,它的底層還是采用字節流來讀取字節,然后它使用指定的編碼方式將讀取字節解碼為字符。在java中Reader是讀取字符流的超類。所以從底層上來看按字節讀取文件和按字符讀取沒什么區別。在讀取的時候字符讀取每次是讀取留個字節,字節流每次讀取一個字節。

字節&字符轉換

字節轉換為字符一定少不了InputStreamReader。API解釋如下:InputStreamReader 是字節流通向字符流的橋梁:它使用指定的 charset 讀取字節并將其解碼為字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認的字符集。 每次調用 InputStreamReader 中的一個 read() 方法都會導致從底層輸入流讀取一個或多個字節。要啟用從字節到字符的有效轉換,可以提前從底層流讀取更多的字節,使其超過滿足當前讀取操作所需的字節。API解釋非常清楚,InputStreamReader在底層讀取文件時仍然采用字節讀取,讀取字節后它需要根據一個指定的編碼格式來解析為字符,如果沒有指定編碼格式則采用系統默認編碼格式。

String file = "C:\\test.txt"; String charset = "UTF-8"; // 寫字符換轉成字節流FileOutputStream outputStream = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset); try { writer.write("我是 cm"); } finally { writer.close(); } // 讀取字節轉換成字符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);

內存
首先我們看下面這段簡單的代碼

String s = "我是 cm"; byte[] bytes = s.getBytes(); String s1 = new String(bytes,"GBK"); String s2 = new String(bytes);

在這段代碼中我們看到了三處編碼轉換過程(一次編碼,兩次解碼)。先看String.getTytes():

public byte[] getBytes() {return StringCoding.encode(value, 0, value.length);}

內部調用StringCoding.encode()方法操作:

static byte[] encode(char[] ca, int off, int len) {String csn = Charset.defaultCharset().name();try {// use charset name encode() variant which provides caching.return encode(csn, ca, off, len);} catch (UnsupportedEncodingException x) {warnUnsupportedCharset(csn);}try {return encode("ISO-8859-1", ca, off, len);} catch (UnsupportedEncodingException x) {// If this code is hit during VM initialization, MessageUtils is// the only way we will be able to get any kind of error message.MessageUtils.err("ISO-8859-1 charset not available: "+ x.toString());// If we can not find ISO-8859-1 (a required encoding) then things// are seriously wrong with the installation.System.exit(1);return null;}}

encode(char[] paramArrayOfChar, int paramInt1, int paramInt2)方法首先調用系統的默認編碼格式,如果沒有指定編碼格式則默認使用ISO-8859-1編碼格式進行編碼操作,進一步深入如下:

String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;

同樣的方法可以看到new String 的構造函數內部是調用StringCoding.decode()方法:

public String(byte bytes[], int offset, int length, Charset charset) {if (charset == null)throw new NullPointerException("charset");checkBounds(bytes, offset, length);this.value =  StringCoding.decode(charset, bytes, offset, length);}

decode方法和encode對編碼格式的處理是一樣的。

對于以上兩種情況我們只需要設置統一的編碼格式一般都不會產生亂碼問題。

編碼&編碼格式
首先先看看java編碼類圖



首先根據指定的chart設置ChartSet類,然后根據ChartSet創建ChartSetEncoder對象,最后再調用 CharsetEncoder.encode 對字符串進行編碼,不同的編碼類型都會對應到一個類中,實際的編碼過程是在這些類中完成的。下面時序圖展示詳細的編碼過程:



通過這編碼的類圖和時序圖可以了解編碼的詳細過程。下面將通過一段簡單的代碼對ISO-8859-1、GBK、UTF-8編碼

public class Test02 {public static void main(String[] args) throws UnsupportedEncodingException {String string = "我是 cm";Test02.printChart(string.toCharArray());Test02.printChart(string.getBytes("ISO-8859-1"));Test02.printChart(string.getBytes("GBK"));Test02.printChart(string.getBytes("UTF-8"));}/*** char轉換為16進制*/public static void printChart(char[] chars){for(int i = 0 ; i < chars.length ; i++){System.out.print(Integer.toHexString(chars[i]) + " "); }System.out.println("");}/*** byte轉換為16進制*/public static void printChart(byte[] bytes){for(int i = 0 ; i < bytes.length ; i++){String hex = Integer.toHexString(bytes[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } System.out.print(hex.toUpperCase() + " "); }System.out.println("");}
}
-------------------------outPut:
6211 662f 20 63 6d 
3F 3F 20 63 6D 
CE D2 CA C7 20 63 6D 
E6 88 91 E6 98 AF 20 63 6D

通過程序我們可以看到“我是 cm”的結果為:

char[]:6211 662f 20 63 6d

ISO-8859-1:3F 3F 20 63 6D
GBK:CE D2 CA C7 20 63 6D
UTF-8:E6 88 91 E6 98 AF 20 63 6D

圖如下:


原文鏈接

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281424.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281424.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281424.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java反射--Class類

面向對象的世界里&#xff0c;萬事萬物皆對象。 1&#xff09;類是誰的對象呢&#xff1f; 類是對象&#xff0c;類是java.lang.Class類的實例對象。 2&#xff09;這個對象如何表示呢&#xff1f; package com.reflect;public class ClassDemo1 {public static void main(Stri…

win10系統按esc會彈出計算機,win10系統版本2004控制面板多出ESC是什么原因?

如果我們的電腦在升級了win102004控制面板多出ESC什么情況方法一&#xff1a;“干凈啟動”&#xff0c;排除第三方軟體的影響1.停止非核心的程序運作(包括第三方殺毒、優化軟體)2.情況允許的話&#xff0c;卸載設備中的第三方殺毒、管家、優化軟件3.同時按【4.點擊【服務】>…

CentOS6/7 配置守護進程

CentOS6.xCentOS6中轉用Upstrat代替以前的init.d/rcX.d的線性啟動方式。一、相關命令通過initctl help可以查看相關命令[rootlocalhost ~]# initctl help Job commands:start Start job.stop Stop job.restart …

Vue源碼解析之數組變異

力有不逮的對象 眾所周知&#xff0c;在 Vue 中&#xff0c;直接修改對象屬性的值無法觸發響應式。當你直接修改了對象屬性的值&#xff0c;你會發現&#xff0c;只有數據改了&#xff0c;但是頁面內容并沒有改變。 這是什么原因&#xff1f; 原因在于&#xff1a; Vue 的響應式…

linux守護進程的編寫

linux監控一個進程進行 代碼如下: #!/bin/shcd /home/autoprocess/ autopgrep -f autoProcessNew.php | wc -l if [ "$auto" 0 ] then nohup php autoProcessNew.php & fi 監視autoProcessNew.php,使他一直監視轉載于:https://www.cnblogs.com/matengfei123/p/…

微軟2014編程之美初賽第一場——題目3 : 活動中心

【來源】 題目3 : 活動中心 【分析】 本題採用的是三分法。 輸入的一組點中找出左右邊界。作為起始邊界。 while(右邊界-左邊界<精度){將左右邊界構成的線段均勻分成3段&#xff0c;推斷切割點的距離關系&#xff0c;抹去距離大的一段。更新左右邊界。 } 輸出左(右)邊界 【…

windows10計算機里輸入法,win10電腦上輸入法不見了怎么辦

好的輸入法可以加快我們的工作效率&#xff0c;當電腦上輸入法不見時&#xff0c;你會調出來嗎?下面小編告訴你win10電腦上輸入法不見時弄出來的一些訣竅吧。win10電腦上輸入法不見了的解決方法win10電腦上輸入法不見了的解決方法&#xff1a;Win10系統輸入法圖標不見了的找回…

Java并發(二十一):線程池實現原理

一、總覽 線程池類ThreadPoolExecutor的相關類需要先了解&#xff1a; &#xff08;圖片來自&#xff1a;https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8%A7%88&#xff09; Executor&#xff1a;位于最頂層&#xff0c;只有一個 execute(Runnable runnable) 方法&a…

進程池

轉自&#xff1a;https://www.cnblogs.com/kaituorensheng/p/4465768.html 在利用Python進行系統管理的時候&#xff0c;特別是同時操作多個文件目錄&#xff0c;或者遠程控制多臺主機&#xff0c;并行操作可以節約大量的時間。當被操作對象數目不大時&#xff0c;可以直接利用…

gulp版本號管理插件注意事項

2019獨角獸企業重金招聘Python工程師標準>>> 打開node_modules\gulp-rev\index.js 第144行 manifest[originalFile] revisionedFile; 更新為: manifest[originalFile] originalFile ?v file.revHash; 打開node_modules\rev-path\index.js 第10行 return filena…

bigfile.to服務器位置,Cloudera Manager 遷移服務器

Cloudera Manager還是比較耗資源的&#xff0c;想把Cloudera Manager&#xff0c;移動到比較好的機器上。在這篇文章中&#xff0c;Cloudera Manager安裝在bigserver1上面&#xff0c;bigserver1是奔騰雙核的CPU。1&#xff0c;Cloudera Manager占資源比較多cloudera manager占…

vue定時ajax獲取數據,vue 中使用 AJAX獲取數據的方法

在VUE開發時&#xff0c;數據可以使用jquery和vue-resource來獲取數據。在獲取數據時&#xff0c;一定需要給一個數據初始值。看下例&#xff1a;new Vue({el:#app,data:{data:""},created:function(){var url"json.jsp";var _selfthis;$.get(url,function…

轉:shell awk

簡單使用&#xff1a; awk &#xff1a;對于文件中一行行的獨處來執行操作 。 awk -F &#xff1a;{print $1,$4} :使用‘&#xff1a;’來分割這一行&#xff0c;把這一行的第一第四個域打印出來 。 詳細介紹&#xff1a; AWK命令介紹 awk語言的最基本功能是在文件或字符串中基…

Mac使用crontab來實現定時任務

crontab 定時執行 配置文件都在/etc/crontab下&#xff0c;如果沒有就創建 語法&#xff1a; crontab [-e [UserName]|-l [UserName]|-r [UserName]|-v [UserName]|File ] 說明&#xff1a; crontab 是用來讓使用者在固定時間或固定間隔執行程序之用&#xff0c;換句話說&#…

前端技術周刊 2018-12-03:DOM

前端快爆 Chrome 71 開始將試用 SXG 功能&#xff0c;它是由 IETF 提出&#xff0c;Web Package 協議規范下的 Signed HTTP Exchanges 功能的縮寫。該技術使得一個第三方服務器可以直接向用戶提供可靠資源&#xff0c;且不用與原站共享 HTTPS 證書密鑰。?點評&#xff1a;一項…

公司新來了一位阿里P9,在全員大會上講葷段子!還是上個世紀的老段子,太爛了!...

阿里P9在坊間的名聲一向不好&#xff0c;這幾年在業界出了不少令人無語的新聞&#xff0c;今天又來了一個&#xff1a;公司新來了一位阿里P9偽高管&#xff0c;全員大會上來先講了一個葷段子&#xff0c;這個破段子還是上個世紀的&#xff0c;太爛了&#xff01;關于這個段子&a…

【轉】博客美化(1)基本后臺設置與樣式設置

閱讀目錄 1.博客園后臺設置2.自定義樣式的設置博客園美化相關文章目錄&#xff1a;博客園博客美化相關文章目錄 一直都拜膜那些博客園的皮膚設計高手&#xff0c;由于本人對前端研究甚少&#xff0c;所以js,css這種東西只能看得懂最基本的&#xff0c;會簡單改改。然后一直對自…

Airdoc創始人:工智能可以在醫療領域多個環節發揮作用 但有局限性

7月1日&#xff0c;在由武漢國家生物產業基地建設管理辦公室主辦、火石創造承辦、光谷健康智慧園協辦的醫療大數據與醫學人工智能高峰論壇上&#xff0c;Airdoc創始人兼董事長張大磊做了題為《AI在醫療領域中應用的問題與局限》的演講。 Airdoc是醫療領域人工智能領軍企業&…

我的世界服務器抽獎系統怎么弄,我的世界自動識別貨幣抽獎機如何制作

我的世界是一款很經典的沙盒類游戲&#xff0c;在游戲中紅石和命令方塊是這部作品的核心&#xff0c;可以制作很多裝備和道具&#xff0c;下面給大家分享下我的世界自動識別貨幣抽獎機如何制作&#xff0c;希望對大家有所幫助。自動識別貨幣抽獎機制作方法廢話不多說,(貌似一句…

Java并發編程中volatile實現過程詳細解析

2019獨角獸企業重金招聘Python工程師標準>>> 首先并發編程有三大特性&#xff1a; 可見性&#xff0c;有序性&#xff0c;原子性。volatile關鍵字實現了前面兩個特性。那么它是如何實現這兩個特性的呢&#xff1f; 首先是可見性。可見性主要是讓緩存&#xff0c;直接…