2012-07-03 3 views
7

Следующий кодОшибка Java? Зачем нужен дополнительный нулевой байт в кодировке utf8?

public class CharsetProblem { 
public static void main(String[] args) { 
    //String str = "aaaaaaaaa"; 
    String str = "aaaaaaaaaa"; 
    Charset cs1 = Charset.forName("ASCII"); 
    Charset cs2 = Charset.forName("utf8"); 

    System.out.println(toHex(cs1.encode(str).array())); 
    System.out.println(toHex(cs2.encode(str).array())); 

} 

public static String toHex(byte[] outputBytes) { 

    StringBuilder builder = new StringBuilder(); 

    for(int i=0; i<outputBytes.length; ++i) { 
     builder.append(String.format("%02x", outputBytes[i])); 
    } 

    return builder.toString(); 
} 
} 

возвращает

61616161616161616161 
6161616161616161616100 

т.е. UTF8 кодирование возвращает избыточный байт. Если взять меньше a-s, то у нас не будет лишних байтов. Если взять больше a-s, мы можем получить все больше лишних байтов.

Почему?

Как можно обойти это?

ответ

6

Вы не можете просто получить массив опор и использовать его. У ByteBuffers есть capacity, position and a limit.

System.out.println(cs1.encode(str).remaining()); 
System.out.println(cs2.encode(str).remaining()); 

производит:

10 
10 

Попробуйте вместо этого:

public static void main(String[] args) { 
    //String str = "aaaaaaaaa"; 
    String str = "aaaaaaaaaa"; 
    Charset cs1 = Charset.forName("ASCII"); 
    Charset cs2 = Charset.forName("utf8"); 

    System.out.println(toHex(cs1.encode(str))); 
    System.out.println(toHex(cs2.encode(str))); 
} 

public static String toHex(ByteBuffer buff) { 
    StringBuilder builder = new StringBuilder(); 
    while (buff.remaining() > 0) { 
    builder.append(String.format("%02x", buff.get())); 
    } 
    return builder.toString(); 
} 

Он производит ожидаемый:

61616161616161616161 
61616161616161616161 
6

Вы считаете, что массив подстановки для ByteBuffer - это точно правильный размер для хранения содержимого, но это необязательно. На самом деле, содержимое даже не нужно начинать с первого байта массива! Изучите API для ByteBuffer, и вы поймете, что происходит: содержимое начинается с значения, возвращаемого arrayOffset(), а конец возвращается limit().

2

Ответ уже был дан, но, как я побежал в ту же проблему, я думаю, это может полезно указать более подробную информацию:

Байт-массив, возвращаемый вызовом cs1.encode(str).array() или cs2.encode(str).array(), возвращает ссылку на весь массив, выделенный для ByteBuffer в это время. Емкость массива может быть больше, чем фактически используется. Чтобы получить только использованную часть, вы должны сделать что-то вроде следующего:

ByteBuffer bf1 = cs1.encode(str); 
ByteBuffer bf2 = cs2.encode(str); 
System.out.println(toHex(Arrays.copyOf(bf1.array(), bf1.limit()))); 
System.out.println(toHex(Arrays.copyOf(bf2.array(), bf2.limit()))); 

Это дает ожидаемый результат.

Смежные вопросы