1. 程式人生 > >JAVA網路傳輸亂碼問題

JAVA網路傳輸亂碼問題

轉自:http://1035054540-qq-com.iteye.com/blog/1856060

JAVA編解碼

                          ---- 亂碼問題

---- 通過一個事例進行分析

一、需求:

二、過程分析:

第一步:java檔案編碼格式

   檔案格式非固定:

第二步:java檔案編寫儲存

第三步:編譯成Class檔案

第四步:load class檔案到JVM

第五步:記憶體

1、java檔案中的字串

2、執行時從網路中讀取到記憶體中的字串

3、執行時從本地檔案中讀取到記憶體中的字串

4、執行時將記憶體中的字串寫入到檔案中

通過一個事例進行分析:

一、需求:

    IDE:

          Myeclipse 10.0

    需求:

     編寫一個java檔案,用來讀取網路資源、本地檔案a以及java檔案中的字串。然後輸出讀取的字串到檔案b.txt中。

二、過程分析:

第一步:java檔案編碼格式

    檔案格式非固定:

     Java檔案在編寫之前需要指定檔案的編碼格式,預設編碼和當前作業系統平臺編碼保持一致。比如,當前作業系統平臺為windows中文版,那麼編碼一般為GBK。當然可以對儲存檔案的編碼進行修改。例如修改成UTF-8。那麼此時檔案儲存的編碼就為UTF-8

第二步:java檔案編寫儲存

java檔案中,有讀取網路流的方法、讀取本地檔案的方法及其輸出字串到檔案中的方法;編寫完成之後,那麼則以第一步的編碼進行儲存。另外,當前java檔案中的所有字串則以第一步中的編碼得以儲存。比如說當前java檔案中有 String str=abc中國;第一步的編碼設定為UTF-8,那麼則以UTF-8進行儲存。如果是GBK,那麼則以GBK進行儲存。

    第三步:編譯成Class

   編譯後的class檔案的編碼固定為UTF-8;和java檔案編碼格式無關。說明,編譯器在編譯的過程中將檔案格式做了處理。編譯器的這種處理操作不會帶來亂碼問題,因為我們必須要相信編譯器的編解碼處理過程。

    第四步:Load class 檔案到jvm

   jvm中的所有字串編碼都為unicode。所以的話,從class檔案再到jvm。編碼又做了一次處理。同樣也必須相信這種處理不會為亂碼留下伏筆;

    第五步:記憶體

   記憶體中執行的是jvm中的資料,jvm中的資料編碼為unicode。那麼記憶體中同樣也以unicode方式進行儲存。但是有一個問題,記憶體執行的過程中,可能會設計到讀取檔案內容、網路內容以及輸出這些內容的操作。在記憶體中讀取的網路內容、檔案內容會以不同的編碼出現。這種編碼和java檔案中處理的方式有關。這兒是亂碼問題出現原因的一部分。記憶體中還有輸出字串內容到檔案的操作。這兒也會存在問題。

 詳細來看:

1、java檔案中的字串

Java程式碼  收藏程式碼
  1. String abc = "abc中國";  
  2. byte[] bytes = abc.getBytes();  
  3. for (byte b : bytes) {  
  4.     System.out.print(b+"  ");  
  5. }  
  6. System.out.println();  
  7. String newabc=new String(abc);  
  8. System.out.println(newabc);     
  9.      Java檔案編碼為UTF-8:  
  10.      列印結果:  
  11.      97  98  99  -28  -72  -83  -27  -101  -67    
  12.      abc中國  
  13.      Java檔案編碼為GBK:  
  14.      列印結果:  
  15.      97  98  99  -42  -48  -71  -6    
  16.      abc中國  
  17. 注:java檔案的編碼可以通過選擇java檔案右鍵Properties——》Text file encoding 中進行設定  

  出現上面列印結果的原因分析如下;

   假如當前java檔案的編碼是gbk,那麼"abc中國"則以gbk格式進行儲存。

abc.getBytes() 方法將字串轉換成位元組處理方式是以當前平臺的編碼進行處理,而在選擇java檔案右鍵Properties——》Text file encoding 中進行設定的編碼就是此時java檔案平臺的編碼。

abc.getBytes() 的本質是 abc.getBytes( Charset.defaultCharset()) ;

它們兩者是等效的 。因為我們當前的編碼設定為gbk,那麼就等效於abc.getBytes(gbk)。也就是說abc.getBytes()等價於abc.getBytes(gbk)

同樣值得注意的是String newabc=new String(bytes); 這兒也進行了預設操作處理。String newabc=new String(bytes) 等效於String newabc=new String(bytes,Charset.defaultCharset()) 因為當前編碼是gbk

String newabc=new String(bytes) 等效於 String newabc=new String(bytes,gbk)

    理一下思路: 

abc中國 以gbk編碼儲存——》以utf-8 編碼的class檔案存在——》以unicode編碼loadjvm中——》同樣以unicode的形式存在於記憶體中——》再以gbk編碼轉成位元組——》最後以gbk編碼轉成字串;

因為最後兩步字串轉成位元組和位元組轉成字串的編碼是統一的,都為gbk。所以不會有亂碼的產生。亂碼產生的原因就是最後兩步字串的編解碼是不統一的。假如字串變成位元組的過程採用gbk編碼,而最後位元組變成字串以utf-8的形式編碼。那麼肯定會出亂碼問題。下面事例就是:

ReadJavaString.java

Java程式碼  收藏程式碼
  1. String abc = "abc中國";  
  2.     byte[] bytes = abc.getBytes("gbk");  
  3.     for (byte b : bytes) {  
  4.         System.out.print(b+"  ");  
  5.     }  
  6.     System.out.println();  
  7.     String newabc=new String(abc,"utf-8");  
  8.     System.out.println(newabc);     
  9.       列印結果如下:  
  10.       97  98  99  -42  -48  -71  -6    
  11.       abc?й?  

  結論:避免亂碼出現問題的解決辦法就是統一編碼。

  字串——位元組     位元組——字串 用同一種編碼

  2、執行時從網路中讀取到記憶體中的字串

      假如需求為:在遠端伺服器中儲存著一個編碼為gbk的 wsx.txt 檔案,要將wsx.txt 檔案中的內容讀取到本地進行列印或者儲存。wsx.txt 中的內容為abc

ReadResourceFromNetWork.java

Java程式碼  收藏程式碼
  1.      URL url =null;  
  2. try {  
  3.     url = new URL("http://wangshuxiong.jhost.cn/wsx.txt");  
  4.     URLConnection urlconnection = url.openConnection();  
  5.     InputStream ins = urlconnection.getInputStream();  
  6.          int a=0;  
  7.     while ((a = ins.read()) != -1) {  
  8.         System.out.print(a+" ");  
  9.     }  
  10.     ins.close();  
  11. catch (Exception e) { }  
  12.      列印結果:97 98 99 214 208   
  13.      此時當前ReadResourceFromNetWork.java 檔案的編碼為gbk。改變ReadResourceFromNetWork.java 的編碼為utf-8 的時候,我們發現一個現象。列印結果依舊為 :      97 98 99 214 208  
  14.      當我們改變wsx.txt 的編碼為utf-8 ,內容依舊為“abc中” 不論ReadResourceFromNetWork.java檔案的編碼是utf-8 還是gbk,那麼列印結果都為:  
  15.                97 98 99 228 184 173   
  16. catch (Exception e) { }  
Java程式碼  收藏程式碼
  1. 由此我們得出一個結論:  
  2.        從網路中讀取資原始檔的時候,無論當前java檔案編碼為何值,我們最後得到的一個個位元組只與讀取的資原始檔儲存的編碼有關。  
  3.        那麼我們可以知道的是:下面wsx.txt 編碼為utf-8 的時候,那麼讀取的位元組陣列bytes 中的編碼為utf-8;  
  4.        URL url =null;  
  5.         try {  
  6.             url = new URL("http://wangshuxiong.jhost.cn/wsx.txt");  
  7.             URLConnection urlconnection = url.openConnection();  
  8.             InputStream ins = urlconnection.getInputStream();  
  9.            byte[] bytes=new byte[ins.available()];  
  10.            int len= ins.read(bytes); /*返回的len是儲存到bytes陣列中實際的長度,比如說bytes陣列定義長度為1024,但是隻讀取了100個位元組長度,那麼則返回的len為100,len最大值為bytes陣列初始長度*/  
  11.             ins.close();  
  12.   當前ReadResourceFromNetWork.java編碼為gbk的時候;呼叫下面方法的話肯定會是亂碼,前面說過,String newabc=new String(bytes)  等效於String newabc=new String(bytes,Charset.defaultCharset());即為GBK編碼,兩者編碼不統一,亂碼是必然,列印結果如下: abc涓?  

要解決上面亂碼問題,方法很簡單,不是說兩者統一就行了嘛。既然讀取位元組的時候無法改變位元組讀取的編碼,事實上也是萬萬不能改變的。那麼我們就改變位元組變成字串時候的編碼

String newabc=new String(bytes,utf-8);

列印結果如下:

abc中 。 

小結一下:

對於讀取網路資源亂碼問題,如果能夠知道資源的編碼格式,那麼,只需要在轉成字串的過程中使用這種編碼就行。所以,關鍵問題落在了判斷資原始檔編碼方式是那種。

有些檔案是由BOMbyte order mark 位元組序標記)的,那麼我們只需要判斷檔案的BOM 就行。 比如說UTF-8 BOM 是前三個位元組為:-17、-69-65GBK 則前兩個位元組為 -1  -2所以一個檔案含有ROM 的話,我們只需要判斷位元組序列標示就行。但是往往某些資原始檔是沒有位元組序列標示符的。所以,就得考慮其他的方式解決了。

先看一些補充知識:

 Unicode是字符集,全世界所有通用存在的文字都有一個唯一的標示符。

 它的編碼範圍是0000-FFFF 。兩個位元組

 各個國家語言的編碼範圍參照如下:

中文的編碼範圍:4e00~9fff  大概有兩萬多個字。

 Ascii的範圍為0-127 

 GBK編碼中,一個漢字用兩個位元組來標示,一個英文字元用一個位元組表示,說白了就  是ascii值。

 前一個位元組十進位制的範圍是:128-254 ,第二個位元組十進位制範圍是64-254

 UTF-8編碼中:一個漢字三個位元組標示 

 第一個位元組十進位制範圍: 224-255 ,第二、三個位元組十進位制 128-255

 GBK 和 unicode的關係是存在一個鍵值對錶儲存gbk 十六進位制和unicode十六進位制的關係。然後通過unicode編碼和中文對照關係,則可以通過一個gbk編碼得到對應的中文漢字。

  流程如下:

       GBK-Unicode對應表                        Unicode

通過gbk——獲得unicode編碼值——通過unicode編碼值獲得中文漢字

UTF-8 和Unicode的轉換規則關係如下:

Unicode符號範圍(十六進位制

UTF-8編碼方式(二進位制)

 0000 - 007F

0xxxxxxx

 0080 - 07FF

110xxxxx 10xxxxxx

0800 - FFFF

1110xxxx 10xxxxxx 10xxxxxx

因為中文的unicode範圍為:4e00~9fff。所以中文是以三個位元組來儲存的。X是unicode對應二進位制依次填充的

例如:Unicode 編碼FFFF 二進位制:1111  1111  1111  1111

填充的UTF-8 為:11101111  10111111  10111111

小結: gbk unicode之間的轉換是通過gbk unicode對映表。

       Utf-8 與unicode之間的轉換是通過轉換規則公式

       所以說,unicode是核心中介。Gbk要轉換成utf-8的話,先轉成unicode。然後unicode再轉換成utf-8;反之亦然。

繼續接著討論怎麼判斷讀取資原始檔格式的問題:

1、當讀取的位元組在0-127範圍的話,說明是ascii字元。直接通過 char c=(char) ?轉換就行;

2、如果位元組 大於127 ,那麼則判斷位元組是不是在128到224範圍內,如果是的話,說明是GBK編碼。因為utf-8的第一個位元組範圍是224255範圍內的。

3、如果不在128224範圍內,接著判斷第二個位元組,如果第二個位元組在64128範圍內的話,那麼則為GBK 編碼。因為UTF-8 的第二三個位元組範圍是128--255.獲取這兩個位元組,轉成十六進位制,再通過gbk unicode對映表就可以得到unicode值,再通過unicode值就可以得到中文漢字。

4、如果第二個位元組依舊不在64128範圍內。那麼則判斷第三個位元組。如果第三個位元組在0--127範圍的話,說明前兩個位元組為gbk編碼。因為gbk編碼是兩個位元組。

5、如果第三個位元組大於127的話,說明這三個位元組為utf-8編碼。然後通過utf-8 unicode的轉碼規則公式換算成unicode,然後通過unicode得到中文漢字。

  3、執行時從本地檔案中讀取到記憶體中的字串

和網路中讀取的結果完全一樣,參考其上!

相關推薦

JAVA網路傳輸亂碼問題

轉自:http://1035054540-qq-com.iteye.com/blog/1856060 JAVA編解碼                           ---- 亂碼問題 ---- 通過一個事例進行分析 一、需求: 二、過程分析: 第一

java網路傳輸字元編碼亂碼解決

1.需求:後臺使用java編碼,傳遞到前臺使用js接收解碼 解決: 1)後臺:json字串形式傳遞   ①簡單的java物件轉化成jsonObject,再toString() JSONObject rtObject = new JSONObject();rtObject.p

java資料傳輸時中文亂碼

在傳送mq時,傳入資料正常,但是消費者接收的資料是亂碼的,可以在傳送的時候對資料進行編碼,然後消費者在進行解碼即可 java中編碼:URLEncoder.encode(URLEncoder.encode("傳遞的中文","utf-8")); java中解碼碼:URLDecoder.decode(

網路傳輸中文亂碼問題

一、解決亂碼問題,要先了解一些基礎概念: 1、字符集:在計算機底層中資料儲存的都是二進位制資料,要想獲取真正有意義的字元,就必須讓二進位制資料與每一個字元對應起來,這種對應關係就形成了一張編碼表。       常用字符集: iso-8859-1  拉丁碼錶 latin,

JAVA網路資訊傳輸加密演算法

為什麼要進行網路加密? 下面來說一下我的實現思想: 加密: 1、第一個假資料長度n+n個隨機字串+真實資料的位元組流+真實資料MD5加密後的串+長度位m第二個假資料+第二個家資料的長度m 2、上述資料在進行跟字母t的按位異或  例項: 816249737104

java 頁面傳輸中文亂碼解決方案

post 中文亂碼解決方式 接受資料的時候設定 request.setCharacterEncoding("utf-8");//編碼必須和頁面編碼一致 頁面設定 <%@page import="java.net.URLDecoder"%> <%@page

java網路程式設計之Netty流資料的傳輸處理(五)

Netty流資料的傳輸處理 Socket Buffer的缺陷       對於例如TCP/IP這種基於流的傳輸協議實現,接收到的資料會被儲存在socket的接受緩衝區內。不幸的是,這種基於流的傳輸緩衝區並不是一個包佇列,而是一個位元組佇列。這意味著,即使

Java在進行網路傳輸的過程中的奇技淫巧

今天,翻書的時候看到書上的網路傳輸資料的時候,頓時感覺之前的自己的操作太low了,這篇主要講,在網路傳輸的過程中,你傳送請求,但是你現在介面又需要根據返回的資料來更新介面,但資料什麼時候返回你又不知道,這時候該怎麼辦?之前我直接在主執行緒裡面一個死迴圈判斷接受資

Android網路傳輸中必用的兩個加密演算法:MD5 和 RSA (附java完成測試程式碼)

MD5和RSA是網路傳輸中最常用的兩個演算法,瞭解這兩個演算法原理後就能大致知道加密是怎麼一回事了。但這兩種演算法使用環境有差異,剛好互補。一、MD5演算法首先MD5是不可逆的,只能加密而不能解密。比如明文是yanzi1225627,得到MD5加密後的字串是:14F2AE15

java網路程式設計TCP傳輸—流操作—拿到源後的寫入動作

在網路程式設計中的TCP傳輸裡,拿到Socket的源後,應該怎麼進行讀寫操作呢,下面我列舉了兩種方法,希望大家幫忙補充···· 1.利用byte陣列作為一個緩衝區進行讀寫 客戶端上傳 1 //獲取

java面試-計算機網路傳輸層知識點全覆蓋

傳輸層概述作用:傳輸層為它上面的應用層提供通訊服務。在OSI七層參考模型中,傳輸層是面向通訊的最高層,也是使用者功能的最底層。傳輸層兩大重要的功能:複用 和 分用。 複用:在傳送端,多個應用程序公用一個傳輸層;分用:在接收端,傳輸層會根據埠號將資料分派給不同的應用程序。和網路

JAVA開發中文亂碼的幾個解決方案

ont character delete bsp mage gbk net utf-8 type 一:html亂碼或者引入的JS亂碼 1:第一步,text file encoding 首先確保文件的保存格式要UTF-8,如在eclipse中,要在文件上點屬性,確保這裏選擇U

java servlet 中文亂碼

window servlet out efault system ava main函數 set print 在servlet中向控制臺輸出中文亂碼; 但是在servlet裏的main函數輸出,中文是正確的; 通過在main函數裏加了一段 System.out.print

java Web 中文亂碼

res utf 同時 訪問 文件的 redirect article nco 16px 參考:http://www.cnblogs.com/haimishasha/p/6117968.html (關於JAVA字符編碼:Unicode,ISO-8859-1,GBK,UTF-

centos java tomcat 中文亂碼解決辦法

title 查詢 發現 即使 art 操作 https size tro 現象: cenos 部署java web 程序 ,java類中有中文 出現亂碼現象 即使使用: System.getProperty("中文") 控制臺都出現 ??????

網路傳輸的中介:代理、閘道器、通道

轉載地址:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html 代理(Proxy): 一箇中間程式,它可以充當一個伺服器,也可以充當一個客戶機,為其它客戶機建立請求。請求是通過可能的翻譯在內部或經過傳遞到其它的

Java 網路IO程式設計

什麼是Socket Socket是應用層與TCP/IP協議族通訊的中間軟體抽象層,它是一組介面。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。 如下

java網路程式設計_IO模型

理解java的BIO、NIO、AIO的原理: 一、UNIX程式設計中的五種IO模型: 1. 阻塞IO 外賣小哥去商家取外賣,到了麻辣燙店,商家還沒做好, 外賣小哥雖然還有其他單子要送,但是不得不焦急地等著商家; 2. 非阻塞IO 外賣小哥去商家取外賣

計算機網路——傳輸

UDP(User Datagram Protocol): 基於Intenet IP協議(複用/分用、簡單的錯誤校驗) 儘可能的服務(可能丟失、亂序到達) 無連線(不需要握手、每個UDP段獨立於其他) 常用於流媒體應用(容忍丟失、速率敏感) UDP用於DNS、SNMP UDP上實現可靠資料傳輸:

tensorflow 中資料經過網路傳輸後的embedding視覺化方法例項:

最近在GitHub上看程式碼偶然發現了使輸入經過網路傳輸後的輸出,即“embedding”視覺化的小細節,在此寫下來加深記憶: Git原連結:https://github.com/ywpkwon/siamese_tf_mnist 首先是建立網路(Siamese 網路): import t