2013-02-09 27 views
1

我試圖用charset解碼器逐字節地解碼UTF8。這可能嗎?使用charset解碼器以逐字節的方式解碼多字節UTF8符號?

以下代碼

public static void main(String[] args) { 

    Charset cs = Charset.forName("utf8"); 
    CharsetDecoder decoder = cs.newDecoder(); 
    CoderResult res; 

    byte[] source = new byte[] {(byte)0xc3, (byte)0xa6}; // LATIN SMALL LETTER AE in UTF8 

    byte[] b = new byte[1]; 
    ByteBuffer bb = ByteBuffer.wrap(b); 

    char[] c = new char[1]; 
    CharBuffer cb = CharBuffer.wrap(c); 

    decoder.reset(); 

    b[0] = source[0]; 
    bb.rewind(); 

    cb.rewind(); 
    res = decoder.decode(bb, cb, false); 

    System.out.println(res); 
    System.out.println(cb.remaining()); 

    b[0] = source[1]; 
    bb.rewind(); 

    cb.rewind(); 
    res = decoder.decode(bb, cb, false); 

    System.out.println(res); 
    System.out.println(cb.remaining()); 



} 

給出以下輸出。

UNDERFLOW 
1 
MALFORMED[1] 
1 

爲什麼?

+0

@jlordo這些原因在這個問題上是不重要的 – 2013-02-09 23:21:36

回答

3

我的理論是與你正在做的方式的問題是,在「溢」狀態,解碼器葉未消費輸入緩衝區中的字節。至少,這是我的閱讀。

注意這句話在Javadoc:

「在任何情況下,如果該方法是在相同的解碼操作,以被再次調用然後,應注意保持殘留在輸入緩衝器中,以便任何字節他們可用於下一次調用「。

但是你正在破壞(可能是)未讀的字節。

您應該能夠通過查看第一個decode(...)調用後bb中未消耗多少字節來檢查我的理論/解釋是否正確。


如果我的理論是正確的,那麼答案就是你不能解碼UTF-8提供的時候只有一個字節一個字節的緩衝區的解碼器。但是,您可以通過從包含一個字節的ByteBuffer開始並添加額外的字節直到解碼器成功輸出字符來實現逐字節解碼。只要確保你沒有破壞尚未被使用的輸入字節。

請注意,這樣的解碼效率不高。 API設計針對一次性解碼大量字節進行了優化。

+0

是的,我現在也注意到了這一點。但奇怪的是這個實現依賴於我將未消耗的字節複製到新的緩衝區。這也意味着緩衝區不能小於解碼的最長字符。特別是這意味着對於逐字節解碼是不可能的。 – 2013-02-10 00:04:16

+0

@SuzanCioc - 不是不可能的。你只需要稍微改變一下。 – 2013-02-10 00:06:01

+0

但是如何?解碼器不會接受一個字節,也不會記住它。所以我有義務用2個字節來提供它(在當前情況下)。所以我需要至少2個字節的緩衝區。沒有辦法按字節進行供稿! – 2013-02-10 00:12:00

2

如前所述,utf的每個char有1-6個字節。你需要的所有字節添加到字節緩衝區你解碼嘗試在此之前:

public static void main(String[] args) { 

    Charset cs = Charset.forName("utf8"); 
    CharsetDecoder decoder = cs.newDecoder(); 
    CoderResult res; 

    byte[] source = new byte[] {(byte)0xc3, (byte)0xa6}; // LATIN SMALL LETTER AE in UTF8 

    byte[] b = new byte[2]; //two bytes for this char 
    ByteBuffer bb = ByteBuffer.wrap(b); 

    char[] c = new char[1]; 
    CharBuffer cb = CharBuffer.wrap(c); 

    decoder.reset(); 

    b[0] = source[0]; 
    b[1] = source[1]; 
    bb.rewind(); 

    cb.rewind(); 
    res = decoder.decode(bb, cb, false); //translates 2 bytes to 1 char 

    System.out.println(cb.remaining()); //prints 0 
    System.out.println(cb.get(0)); //prints latin ae 

} 
+2

UTF-8每個字符有1到6個字節 – 2013-02-09 23:34:40

+0

我怎麼能提前知道應該分配多少個字節?假設我會再添加一個字節,但它也可能看起來不正確。 – 2013-02-09 23:39:56

+1

分配六個字節。只要CharsetDecoder可以一次讀取至少一個完整的字符,它就會很快樂;它會在「ByteBuffer」中留下額外的字節,在那裏你應該壓縮它們。 – 2013-02-09 23:42:11

相關問題