程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> J2ME >> J2ME中文編碼標題

J2ME中文編碼標題

編輯:J2ME

1. 簡介

本文先容JavaME中文編碼的相干標題,這個標題一度是互聯網上的開發者們討論的熱點話題。本文收拾和綜合了網上眾多相干內容,盡可能的為開發者供給一個全面、系統的熟悉。

文中的代碼僅用來闡明原理,可能很不完整,缺乏變量定義或者返回值,請體諒。部分代碼直接起源於網上的其他材料。

感謝眾多開發者在中文編碼標題上做出的努力與摸索。總結中有什麼標題的話,歡迎大家指正:)

2. 術語先容

2.1 ASCII

基於羅馬字母表的一套電子盤算器編碼系統,是單字節的編碼方法,每個ASCII字符占用1個字節(8bits),所以ASCII編碼最多可以表現256個字符。它是美國信息交換尺度委員會(American Standards Committee for InformationInterchange)的縮寫, 為美國英語通信所設計。

顯然ASCII編碼用來表現英文字母和字符是足夠了的,但是對於中文和日文等眾多的文字來說,是遠遠不夠的。

跟ASCII類似的編碼還有ISO8859-1。

2.2 UNICODE

雙字節編碼方法,它為每種語言中的每個字符設定了同一並且唯一的二進制編碼,以滿足跨語言、跨平台進行文本基准轉換、處理的請求。UNICODE支撐歐洲、非洲、中東、亞洲(包含同一尺度的東亞像形漢字和韓國像形文字)的文字。

UNICODE又可以分為“高位在前”和“低位在前”的兩種格局,這和CPU的處理方法有點關系。這一點可以通過BOM(Byte OrderMark)來標示,若采用 “低位在前”方法編碼,BOM 會表現為 0xFF 0xFE,而在 Unicode 的定義中是不存在 U+FFFE這個字符的。若采用高位在前方法編碼,BOM 會表現為 0xFE 0xFF,而 U+FEFF 恰好是在 Unicode中的有效字符,代表的是一個不占空間的 space 符號,所以即使沒被說明為 BOM,也不會對閱覽者產生錯誤的信息。

但UINICODE也帶來一些標題,當美國人看見自己天天最常用的字符需要用兩倍的空間來保留時,自然會感到這是一種浪費,他們必定會說:看看那堆0。於是新的編碼方法又出生了。

2.3 UTF-8

UTF的全稱是UCS Transformation Format,即把Unicode轉做某種格局的意思。目前存在的UTF格局有:UTF-7, UTF-7.5, UTF-8, UTF-16, 以及 UTF-32,本文討論UTF-8格局。

UTF-8是UNICODE的一種變長字符編碼,理論上應用1~6個字節來編碼UNICODE。

固然理論上UTF-8最多為6個字節,但是,由於雙字節的Unicode最大為0XFFFF,所以雙字節的Unicode轉為UTF-8後最長為3個字節。

下列字節串用來表現一個字符。 用到哪個串取決於該字符在 Unicode 中的序號。

U-00000000 - U-0000007F: 0******x U-00000080 - U-000007FF: 110***xx 10****** U-00000800 - U-0000FFFF: 1110***x 10****** 10****** U-00010000 - U-001FFFFF: 11110*** 10****** 10****** 10****** U-00200000 - U-03FFFFFF: 111110xx 10****** 10****** 10****** 10****** U-04000000 - U-7FFFFFFF: 1111110x 10****** 10****** 10****** 10****** 10******

上表中的***為Unicode編碼的二進制數據。例如:“中”字,Unicode編碼為4E2D。

Unicode: 4E 2D          01001110 00101101UTF-8: E4 B8 AD  11100100 10111000 10101101

對於英文來說,UTF-8跟ISO8859-1一樣節儉;但顯然中文等字符將為UTF-8付出更多。

2.4 GB2312

GB2312又稱國標碼,由國家尺度總局宣布,1981年5月1日實行,通行於大陸。新加坡等地也應用此編碼。它是一個簡化字的編碼規范,當然也包含其他的符號、字母、日文假名等,共7445個圖形字符,其中漢字占6763個。我們平時說6768個漢字,實際上裡邊有5個編碼為空缺,所以總共有6763個漢字。

GB2312規定“對任意一個圖形字符都采用兩個字節表現,每個字節均采用七位編碼表現”,習慣上稱第一個字節為“高字節”,第二個字節為“低字節”。GB2312中漢字的編碼范疇為,第一字節0xB0-0xF7(對應十進制為176-247),第二個字節0xA0-0xFE(對應十進制為160-254)。

GB2312具有許多擴大,其中GBK是微軟對GB2312的擴大,GB18030則是2000年宣布的國家尺度,是到目前為止最新的國標漢字編碼。

3.JavaME中的字符編解碼

3.1 編解碼方法

說到JavaME中的字符編碼標題,自然要從String類進手,在String類中我們可以找到字符編解碼的相干方法:

1. 解碼:

public String(byte[] bytes, String enc) throws UnsupportedEncodingException

2. 編碼:

public byte[] getBytes(String enc) throws UnsupportedEncodingException

舉例來說,“諾基亞”三個漢字的GB2312編碼為C5 B5 BB F9 D1 C7。

代碼段一,解碼實驗:

byte[] codes = {0XC5, 0XB5, 0XBB, 0XF9, 0XD1, 0XC7};String string = new String(codes, “gb2312”);testForm.append(string);

得到的成果為:諾基亞

代碼段二,編碼實驗:

byte[] codes = “諾基亞”.getBytes(“gb2312”);for (int i = 0, t = codes.length; i < t; i ++) {    String hexByte = Integer.getHexString(codes[i]);    if (hexByte.length() > 2) { hexByte = hexByte.subString(hexByte.length() - 2);    }    testForm.append(“0X”+ hexByte.subString + “, ”);}

得到的成果為:0XC5, 0XB5, 0XBB, 0XF9, 0XD1, 0XC7,

3.2 檢查設備的編碼支撐情況

一個最直接的獲取編碼支撐的方法是應用System.getProperty(“microedition.encoding”),可以得到設備的默認的字符編碼,以NOKIA設備為例,得到的屬性值為ISO8859-1。

然而,通過這個方法的意義並不大。首先,它只能獲取到一個編碼格局,而一般設備都會支撐許多種編碼規范;其次,這個屬性的數值與虛擬機的實現有很大關系,同樣以NOKIAS40v2為例,不論設備的目標市場應用什麼語言,這個屬性同一為ISO8859-1[參考材料5],顯然ISO8859-1對於中文來說是毫無意義的。

再回過火來看看上一節中的兩個方法,它們都會拋出一個UnsupportedEncodingException。利用這一點,我們可以自己來做一個設備支撐編碼規范情況的測試。

boolean isEncodingSupported (String encoding) {    try { "諾基亞".getBytes(encoding); return true;    } catch (UnsupportedEncodingException uee) { return false;    }}

這裡需要提示的是,對於同一個編碼格局來說,可能會有許多種不同的名稱,例如Unicode在NOKIA的設備上用的是ucs-2,再例如utf-8來說,utf-8、utf8和utf_8都會有可能。對於這一點,CLDC的規范中並沒有給出嚴格的定義。所以在實際測試的過程中需要充分考慮到這個情況。

3.3 readUTF() 和 writeUTF(String)

CLDC中還有兩個方法跟字符編碼有關系:DataInputStream中的readUTF()和DataOutputStream中的writeUTF(String)。根據兩個方法的JavaDoc,writeUTF(String)首先會向輸出流中寫進字符串編碼成UTF8格局後的byte數組長度(2個字節),然後再將這個UTF8的byte數組寫進。而readUTF()則是先從輸進流中讀取2個字節,組成一個short數值,在從輸進流中讀出這個數值長度的byte數據,再將這個byte數組解碼成字符串。具體闡明請參考DataInputStream和DataOutputStream的Java Document。

4.具體標題的解決

上一部分中先容了CLDC平台上所有跟字符編解碼相干的API。懂得了這些內容以後,就可以聯合具體的情況來考慮如何解決中文編碼的標題了。

首先,中文編碼標題中最常見的情況就是亂碼,那麼亂碼是如何產生的呢?無非是以下幾種情況:

    指定的編碼格局與數據實際的編碼格局不符,造成數據被編碼成指定的調換符號或被說明成與源字符完整不同的字符;指定的編碼格局准確,數據不完整或被修正,造成數據無法在字符集中找到與其對應的編碼;

所以,解決中文編碼標題的幾個基礎思路是:

    存儲或傳輸前,確保數據被准確編碼;確保留儲、讀取和傳輸的過程完整、准確;解碼時,應用與編碼時同樣的編碼格局;確認編碼格局是否被設備支撐;

4.1 RMS存儲的中文標題

這個標題完整可以應用readUTF和writeUTF來解決。

用UTF8編碼向RecordStore中寫進中文:

String
content = "中文字符"; ByteArrayOutputStream bos = new ByteArrayOutputStream();DataOutputStream DOS = new DataOutputStream(bos);DOS.writeUTF(content); byte[] bytes = bos.toByteArray();rs.addRecord(bytes, 0, bytes.length);

從RecordStore中讀出UTF8編碼的中文:

byte utfBytes[] = rs.getRecord(dbid);DataInputStream dis=new DataInputStream(new ByteArrayInputStream(utfBytes));String content = dis.readUTF();

當然,應用UTF8對於中文來說意味著更大的存儲空間,所以也可以應用類似Unicode和GB2312等2字節的編碼。在此之前,請通過3.2中描寫的方法檢測編碼是否被支撐。

用GB2312編碼向RecordStore中寫進中文:

String content = "中文字符"; byte[] bytes = content.getBytes("gb2312");rs.addRecord(bytes, 0, bytes.length);

從RecordStore中讀出GB2312編碼的中文:

byte gb2312Bytes[] = rs.getRecord(dbid); String content = new String(gb2312Bytes, "gb2312");

4.2 從Resource文件中讀取中文

大概可以分為兩種情況,一是這個文件遵守某種特定的格局,例如RPG游戲的關卡文件,其中包含輿圖、事件和對話等數據,文件由特定的程序天生,為自有格局。這種情況基礎上可以應用與上一小節中同樣的方法來解決。要害是,存儲和讀取的過程服從同樣的數據和編碼格局。

另一種情況是txt文件,可能通過一些文本編纂工具天生。Txt文件中常見的編碼格局有Unicode、UTF8、Unicode Big Endian等。在我們讀取txt文件之前,最先要確認的就是這個txt文件所應用的編碼格局。

以UTF8為例:

in = getClass().getResourceAsStream(filename);in.read(Word_utf);in.close();string =new String(Word_utf,"UTF-8");

對於Unicode來說,這裡引用了參考1中的一段代碼,這段代碼實際上是在處理“低位在前”的Unicode:

public static String unicodeBytesToString(byte abyte0[], int i){    StringBuffer stringbuffer = new StringBuffer("");    for(int j = 0; j < i; )    {        int k = abyte0[j++]; //留心在這個處所進行了碼制的轉換         if(k < 0)            k += 256;        int l = abyte0[j++];        if(l < 0)            l += 256;        char c = (char)(k + (l << 8));//把高位和低位數組裝起來         stringbuffer.append(c);    }}

對於高位在前的Unicode,可以應用和UTF8類似的方法。請留心,這裡的"ucs-2"是針對諾基亞設備的,其他廠商設備可能與此不同,請查閱相干文檔或自行測試。

in = getClass().getResourceAsStream(filename);in.read(Word_ unicode);in.close();string =new String(Word_unicode, "ucs-2");

實際上經過我在NOKIA設備上的測試,對於低位在前的Unicode,也可以應用這個方法。條件是需要確保在數據的最前端添加低位在前的BOM(0XFFFE)。

持續應用“諾基亞”為例,高位在前和低位在前的的Unicode編碼分辨為:

nokiaBE = {(byte)0x8b, (byte)0xfa, (byte)0x57, (byte)0xfa, (byte)0x4e, (byte)0x9a,}nokiaLE = {(byte)0xfa, (byte)0x8b, (byte)0xfa, (byte)0x57, (byte)0x9a,  (byte)0x4e,};

new String(nokiaBE, "ucs-2")的成果是“諾基亞”,而new String(nokiaLE, "ucs-2")的成果則是亂碼。然後,我們對nokiaLE做出修正:

nokiaLE = {(byte)0xff, (byte)0xfe, (byte)0xfa, (byte)0x8b, (byte)0xfa, (byte)0x57, (byte
     )0x9a, (byte)0x4e,};

修正後,再次履行new String(nokiaLE, "ucs-2",則得到的成果也是“諾基亞”。

BTW,對於Resouce文件來說,固然應用Unicode編碼存儲中文看起來像是比UTF-8要更節儉,但是當Resource資源被打成Jar包時,壓縮後的文件大小可能很接近。

4.3通過網絡讀取中文

和前面描寫的一樣,避免亂碼的要害同樣是保證編解碼應用同樣的格局,也就是客戶端與服務器段保持同樣的字符編碼。

對於socket連接來說,傳輸的內容可以應用自定義的數據格局,所以處理的方法完整和前面兩節是雷同的,甚至可以向http學習,在數據的開頭商定字符編碼。

對於http連接,在http數據頭中已經商定了編碼格局,應用這個編碼格局解碼即可。另外一個很常見的標題,就是從xml文件中解析中文。對於這一點,在kXML2中已經有了很好的解決計劃。

org.kXML2.io.KXMLParser.setInput(InputStream is, String _enc)

你可以通過_enc指定一個編碼格局,假如_enc為null,則Parser會根據數據的特征主動嘗試各種編碼格局。由於kXML為開源項目,假如這裡的處理方法需要調劑,你也可以自己動手往完善它的功效。

在kXML2中也增加了對wml文件的解析,有愛好的可以研究一下,這一部分我沒有作過嘗試。

4.4 JAD中的中文

JAD文件假如需要應用中文,則需要應用UTF8格局。關於如何在JAD中寫進UTF8數據,可以有許多方法,可以應用一些UltralEditor之類的文本工具,或應用一些JAD/MENIFEST天生工具。你甚至可以自己編寫一個JAD/MENIFEST的天生工具,對於JavaSE平台來說,這並不是一件太難的工作,你還可以給這個工具賦予更多的功效,例如主動填寫vendor和version之類的字段。

特別闡明,對於NOKIA SerIEs 40v2等設備來說,通過OTA下載Jad/Jar時,需要在服務器端為JAD添加MIME type時標明encoding為UTF8,否則即使你的JAD確實應用了UTF8格局,安裝之後仍然會是亂碼。

4.5 Unicode和UTF8的互轉

在網上有一些Unicode和UTF8互相轉換的代碼,是直接通過編碼格局往實現的,從前文敘述的Unicode和UTF8之間的關系來看,Unicode和UTF8之間很輕易互相轉換,盤算量也不會太大。這裡就不在把這些代碼貼出來了,有愛好的可以自己找一下,或者自己寫一個。

但是,實際上Unicode和UTF8這兩個編碼格局基礎是所有設備都會支撐的,直接通過String類的編解碼方法互轉就可以了。

4.6設備不支撐的編碼格局

假如需要應用設備不支撐的編碼格局,那麼必定將付出一些額外的代價。例如,假如設備不支撐GB2312,則你可以有如下的幾個選項中選擇其一:

    在你的程序中包含一個GB2312的字符對應表;(也許GB2312你還可以接收,但是GB18030要怎麼辦呢?)做一個代理服務器,將GB2312轉解碼成可以被設備支撐的編碼;放棄GB2312,選擇其他編碼;

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved