Java 7の基礎-符号化と復号化


一般的に符号化と復号に関わるところは文字とバイトが互いに変換されるときであり,一般的に応用される主なシーンはI/Oである.変換がうまくいかないと、いろいろな問題に直面しますが、最もよくあるのは文字化けしです.次にJavaにおける符号化と復号化について深く探究する.
まず、一般的なコードテーブルを見てみましょう.以下のようにします.
ASCII:
アメリカ標準情報交換コード.1バイトの7ビットで表すことができます
ISO8859-1:
ラテン?テーブル欧州コードテーブルは1バイトの8ビットで表される
GB2312:
中国の中国語コード表
GBK:
中国の中国語コード表はアップグレードされ、より多くの中国語文字記号が融合した.
Unicode:
国際標準コードは、多くの文字を融合させた.すべての文字は2バイトで表され、Java言語はunicodeです
UTF-8:
最大3バイトで1文字を表す
Javaプログラムの実行中、文字列オブジェクトは常にUnicode符号化でメモリに保存されますが、文字列オブジェクトを永続化リソース(ファイルまたはデータベース)に保存したり、ネットワークを介して転送したりする場合は、通常バイトで処理されます.これにより、Java APIは両者を交換する機能を提供しなければならない.実際、この機能はStringクラスおよびCharsetクラスで提供されています.
1、Stringクラス
StringクラスのgetBytes()メソッドを使用して、Unicode文字セット符号化から他の文字セット符号化への変換を本質とする異なる文字セットのバイトストリームデータを返します.
String str=" ";
byte[] bytes1 = str.getBytes();              // 1 
byte[] bytes2 = str.getBytes("ISO-8859-1");  // 2
System.out.println(new String(bytes1));      //      
System.out.println(new String(bytes2));      //   "?"
は、オペレーティングシステムのデフォルト符号化方式に従って符号化されるStringクラスのオブジェクトstrに文字列「中」を付与し、中国語windowsシステムでは通常「GBK」であり、「中」はGBK符号化では0 xD 6 D 0であり、この文字をstrに付与するとJavaはその文字列を符号化変換し、GBK符号化方式の「中」をUnicode符号化方式の「中」に変換し、Unicode符号化方式の「中」の符号化は0 x 4 E 2 Dであるため、strがプログラム実行中にメモリ中のバイナリを16進数で表すと0 x 4 E 2 Dとなる.
str文字列のバイナリ形式を取得します.getBytes(String encoding)メソッドは、文字列がどの符号化方式でバイナリ形式を取得するかを示す符号化方式を指定する必要がある.この文にはパラメータが設定されておらず、オペレーティングシステムのデフォルトの符号化方式を採用しているため、GBK符号化のバイナリ形式であるbytes[0]=0 xD 6、bytes[1]=0 xD 0(のGBK符号化は0 xD 6 D 0)を表す.GBKコードも中国語対応なので、出力時に文字化けして表示されません.
符号化方式がISO-8859-1、すなわち通常言うLatin-1を指定した場合、この符号化は8 bit対文字符号化を採用するので、符号化空間には256文字しかない.この符号化には基本的なASCII符号といくつかの拡張された他の西欧文字しか含まれていないため、この文字セットには中国語の「中」字を含めることはできない.つまりJava仮想マシンはISO-8559-1符号セットで「中」字対応の符号化を見つけることができず、この場合、疑問符(?,0 x 3 f)文字だけを返すので、このときbyte.lengthは1のみであり、bytes[0]=0 x 3 fである.
もう1つの例を見てみましょう.
public static void main(String[] args) {  
  
    String str = "  ";  
    printBytes("UNICODE  :", str.getBytes(Charset.forName("unicode")));  
    printBytes("GBK      :", str.getBytes(Charset.forName("GBK")));  
    printBytes("UTF-8  :", str.getBytes(Charset.forName("UTF-8")));  
}  
  
public static void printBytes(String title, byte[] data) {  
    System.out.println(title);  
    for (byte b : data) {  
        System.out.print("0x" + toHexString(b) + " ");  
    }  
    System.out.println();     
}  
  
public static String toHexString(byte value) {  
    String tmp = Integer.toHexString(value & 0xFF);  
    if (tmp.length() == 1) {  
        tmp = "0" + tmp;  
    }  
  
    return tmp.toUpperCase();  
}  
の出力結果は次のとおりです.
中国のUNICODコード:0 xFE 0 xFF 0 x 4 E 0 x 2 D 0 x 56 0 xFD中国のGBKコード:0 xD 6 0 xD 0 xB 9 0 xFA中国のUTF-8コード:0 xE 4 0 xB 8 0 xAD 0 x 9 B 0 xBD
なお、文字列オブジェクトから取り出したUnicode符号化されたバイトストリームデータの場合、その先頭部分にBOM(ByteOrderMark)が存在するが、一般的にこのBOM値は「0 xFE 0 xFF」、すなわち大端バイト順(BIG_ENDIAN)である.BOM値が「0 xFF 0 xFE」の場合、リトルエンドバイト順(LITTLE_ENDIAN)になります.
Stringクラスの構成方法で異なる文字セットのバイトストリームデータから文字列オブジェクトを生成することもでき、
その本質は,他の文字セット符号化からUnicode文字セット符号化への変換過程である.
一般的な形式は次のとおりです.
new String(byte[] bytes, String encoding)
の例を見てみましょう.
byte[] bytes = {(byte)0xD6, (byte)0xD0, (byte)0x31};
String str1 = new String(bytes);
String str2= new String(bytes,"ISO-8859-1");
System.out.println(str1);   //  1
System.out.println(str2);   // ??1

この方法はencoding符号化法に従ってバイト配列bytesのバイナリ配列を解析し,新しい文字列オブジェクトを生成する.このバイト配列におけるバイナリデータをデフォルトの符号化方式(GBK)で文字列に符号化すると、GBKでは0 xD 6 0 xD 0が「中」、0 x 31が「1」(GBKはASCII対応だが、ISO-8859-1はASCII以外の部分に対応していないことが分かるので、str 1が得られる値は「中1」である
この文はISO-8859-1符号化方式でこのバイトデータを符号化しているが、ISO-8859-1符号化方式では1バイトが1文字に解析されるため、このバイト配列は3文字の文字列を含むように解釈されるが、ISO-8859-1符号化方式では0 xD 6と0 xD 0に対応する文字がないため、最初の2文字に2つの疑問符が発生し、0 x 31はISO-8859-1符号化に対応する文字「1」(ISO-8859-1もASCIIに対応)であるため、この文で得られるstrの値は「?1」である.
2、Charset類
現在のJavaクラスにはCharsetクラスが提供されており、encodeとdecodeはそれぞれchar[]からbyte[]への符号化とbyte[]からchar[]への復号を提供している.次のコードを示します.
Charset charset = Charset.forName("UTF-8"); 
ByteBuffer byteBuffer = charset.encode(string); 
CharBuffer charBuffer = charset.decode(byteBuffer);
の符号化および復号は、いずれも1つのクラスで完了し、forName()メソッドによって符号化文字セットを設定することで、符号化フォーマットを統一しやすくなります.
Javaにはもう一つのByteBufferクラスがあり、charとbyteの間のソフト変換を提供している.それらの間の変換は符号化と復号を必要とせず、16 bitのcharフォーマットを2つの8 bitのbyte表現に分割しただけで、実際の値は変更されず、データのタイプだけが変換された.コードは次のとおりです.
ByteBuffer heapByteBuffer = ByteBuffer.allocate(1024); 
ByteBuffer byteBuffer = heapByteBuffer.putChar(c);

これらは、文字とバイト間の相互変換を提供し、符号化フォーマットの統一を設定すれば一般的に問題はありません.
3、まとめ
Stringクラスを使用する場合:
エンコーディング:文字列がバイト配列になります.String -->byte[]; (Stringクラスのメソッド):str.getBytes(charsetName);この文字列を指定したエンコーディングテーブルに従って符号化します.復号化:バイト配列が文字列になります.byte[] -->String; (Stringクラスの構築方法):new String(byte[],charsetName);指定したcharsetを使用して指定したbyte配列を復号します.
Charsetクラスを使用する場合:
クラスにはencodeとdecodeがそれぞれchar[]からbyte[]への符号化とbyte[]からchar[]への復号を提供する.まずforName()を使用して、エンコードおよびデコード文字セットを設定します.
参考文献:
1、http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/
2、http://blog.sina.com.cn/s/blog_6ab818cb0100qis5.html