我对CharsetDecoder
类有问题。
第一个代码示例(有效):
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final ByteBuffer b = ByteBuffer.allocate(3);
final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
for (int i=0; i<tab.length; i++){
b.put(tab, i, 1);
}
try {
b.flip();
System.out.println("a" + dec.decode(b).toString() + "a");
} catch (CharacterCodingException e1) {
e1.printStackTrace();
}
结果为a€a
但是当我执行此代码时:
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(3);
final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
for (int i=0; i<tab.length; i++){
ByteBuffer buffer = ByteBuffer.wrap(tab, i, 1);
dec.decode(buffer, chars, i == 2);
}
dec.flush(chars);
System.out.println("a" + chars.toString() + "a");
结果为a
为什么结果不一样?
如何使用类decode(ByteBuffer, CharBuffer, endOfInput)
的方法CharsetDecoder
来检索结果a€a
?
-编辑-
因此,我使用Jesper的代码。这不是完美的方法,但可以使用step
= 1、2和3
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(6);
final byte[] tab = new byte[]{(byte)97, (byte)-30, (byte)-126, (byte)-84, (byte)97, (byte)97}; //char €
final ByteBuffer buffer = ByteBuffer.allocate(10);
final int step = 3;
for (int i = 0; i < tab.length; i++) {
// Add the next byte to the buffer
buffer.put(tab, i, step);
i+=step-1;
// Remember the current position
final int pos = buffer.position();
int l=chars.position();
// Try to decode
buffer.flip();
final CoderResult result = dec.decode(buffer, chars, i >= tab.length -1);
System.out.println(result);
if (result.isUnderflow() && chars.position() == l) {
// Underflow, prepare the buffer for more writing
buffer.position(pos);
}else{
if (buffer.position() == buffer.limit()){
//ByteBuffer decoded
buffer.clear();
buffer.position(0);
}else{
//a part of ByteBuffer is decoded. We keep only bytes which are not decoded
final byte[] b = buffer.array();
final int f = buffer.position();
final int g = buffer.limit() - buffer.position();
buffer.clear();
buffer.position(0);
buffer.put(b, f, g);
}
}
buffer.limit(buffer.capacity());
}
dec.flush(chars);
chars.flip();
System.out.println(chars.toString());
decode(ByteBuffer, CharBuffer, boolean)
方法返回结果,但是您忽略了该结果。如果在第二个代码片段中打印结果:
decode(ByteBuffer, CharBuffer, boolean)
您将看到此输出:
for (int i = 0; i < tab.length; i++) {
ByteBuffer buffer = ByteBuffer.wrap(tab, i, 1);
System.out.println(dec.decode(buffer, chars, i == 2));
}
显然,如果您在字符中间开始解码,它将无法正常工作。解码器希望它读取的第一件事是有效的UTF-8序列的开始。
edit-解码器报告UNDERFLOW
MALFORMED[1]
MALFORMED[1]
a a
时,它希望您将更多数据添加到输入缓冲区中,然后尝试再次调用UNDERFLOW
,但是您必须从...开始重新为其提供数据。您尝试解码的UTF-8序列。您不能在UTF-8序列的中间继续。
这里是一个有效的版本,在循环的每次迭代中都从decode()
中增加一个字节:
tab
解码器不会在内部缓存部分字符的数据,但这并不意味着您必须做复杂的事情才能确定要重新馈送解码器的数据。您提供了一种清晰的方法来表示其实际消耗了哪些数据,即输入ByteBuffer及其位置。在第二个示例中,每当OP未能通过解码器时,通过给它一个新的ByteBuffer,它报告它尚未消耗的内容。
使用NIO缓冲区的标准模式是输入,翻转,输出,压缩,循环。缺乏优化(可能还为时过早),没有理由重新实现紧凑的自己。您可能会弄错它,就像@Jesper和@lecogiteur一样(如果曾经提供过多个字符)。您不应该从解码调用之前重置到该位置。
第二个示例应该阅读如下内容:
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(3);
final byte[] tab = new byte[]{(byte) -30, (byte) -126, (byte) -84}; //char €
final ByteBuffer buffer = ByteBuffer.allocate(10);
for (int i = 0; i < tab.length; i++) {
// Add the next byte to the buffer
buffer.put(tab[i]);
// Remember the current position
final int pos = buffer.position();
// Try to decode
buffer.flip();
final CoderResult result = dec.decode(buffer, chars, i == 2);
System.out.println(result);
if (result.isUnderflow()) {
// Underflow, prepare the buffer for more writing
buffer.limit(buffer.capacity());
buffer.position(pos);
}
}
dec.flush(chars);
chars.flip();
System.out.println("a" + chars.toString() + "a");
NOTE:上面的内容不检查返回值以检测格式错误的输入或其他错误处理,以便在任意输入/ IO条件下安全运行。