¿Cómo puedo codificar / decodificar UTF-16LE matrices de bytes con una lista de materiales?

StackOverflow https://stackoverflow.com/questions/879482

Pregunta

Necesito para codificar / decodificar UTF-16 matrices de bytes hacia y desde java.lang.String. Las matrices de bytes se dan a mí con una byte de marcas de pedido (BOM) , y necesito a matrices de bytes codificados con una lista de materiales.

Además, debido a que estoy tratando con un cliente de Microsoft / servidor, me gustaría emitir la codificación en Little Endian (junto con la lista de materiales LE) para evitar cualquier malentendido. Soy consciente de que con la lista de materiales que debería funcionar big endian, pero yo no quiero nadar contra la corriente en el mundo de Windows.

Como ejemplo, aquí es un método que codifica una java.lang.String como UTF-16 en poco endian con una lista de materiales:

public static byte[] encodeString(String message) {

    byte[] tmp = null;
    try {
        tmp = message.getBytes("UTF-16LE");
    } catch(UnsupportedEncodingException e) {
        // should not possible
        AssertionError ae =
        new AssertionError("Could not encode UTF-16LE");
        ae.initCause(e);
        throw ae;
    }

    // use brute force method to add BOM
    byte[] utf16lemessage = new byte[2 + tmp.length];
    utf16lemessage[0] = (byte)0xFF;
    utf16lemessage[1] = (byte)0xFE;
    System.arraycopy(tmp, 0,
                     utf16lemessage, 2,
                     tmp.length);
    return utf16lemessage;
}

¿Cuál es la mejor manera de hacer esto en Java? Idealmente me gustaría evitar la copia de toda la matriz de bytes en una nueva matriz de bytes que tiene dos bytes adicionales asignados por el principio.

Lo mismo vale para la decodificación de una cadena tal, pero eso es mucho más fácil mediante el uso de la java.lang.String constructor :

public String(byte[] bytes,
              int offset,
              int length,
              String charsetName)
¿Fue útil?

Solución

El "UTF-16" nombre de conjunto de caracteres siempre codificará con una lista de materiales y decodificará los datos utilizando ya sea grande poco endianness /, pero "UnicodeBig" y "UnicodeLittle" son útiles para la codificación en un orden de bytes específico. Utilice UTF-16LE o UTF-16BE para no BOM - ver esto puesto para el uso de '\ uFEFF' para manejar listas de materiales manualmente. Ver aquí para la denominación canónica de juego de caracteres nombres de cadena o (preferiblemente) la Charset clase. También tome en cuenta que sólo un subconjunto limitado de codificaciones se requieren absolutamente para ser compatibles.

Otros consejos

Esta es la forma de hacerlo en NIO:

    return Charset.forName("UTF-16LE").encode(message)
            .put(0, (byte) 0xFF)
            .put(1, (byte) 0xFE)
            .array();

Se supone que es sin duda más rápido, pero no sé cuántos matrices se hace bajo las sábanas, pero mi comprensión del punto de la API es que se supone que para reducir al mínimo.

En primer lugar, para la decodificación se puede usar la codificación "UTF-16"; que detecta automáticamente una lista de materiales inicial. Para la codificación UTF-16BE, también puede utilizar el juego de caracteres "UTF-16" - que va a escribir una lista de materiales adecuada y cosas grandes endian entonces la salida

.

Para la codificación a little endian con una lista de materiales, no creo que el código actual es demasiado malo, incluso con el doble de la asignación (a menos que las cadenas son verdaderamente monstruosa). Lo que usted podría querer hacer si no se está frente a una matriz de bytes sino más bien una java.nio ByteBuffer, y utilizar la clase java.nio.charset.CharsetEncoder. (Que se puede obtener de Charset.forName ( "UTF-16LE"). NewEncoder ()).

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(string.length() * 2 + 2);
    byteArrayOutputStream.write(new byte[]{(byte)0xFF,(byte)0xFE});
    byteArrayOutputStream.write(string.getBytes("UTF-16LE"));
    return byteArrayOutputStream.toByteArray();

EDIT: Al releer su pregunta, veo que prefiere evitar la doble asignación gama por completo. Por desgracia, la API no le da que, por lo que yo sé. (Hubo un método, pero está en desuso, y no se puede especificar la codificación con él).

escribí lo anterior antes de ver su comentario, creo que la respuesta a utilizar las clases nio está en el camino correcto. Estaba buscando en eso, pero no estoy lo suficientemente familiarizado con la API de saber cómo fuera de la mano a hacerlo bien.

Esta es una vieja pregunta, pero aún así, no pude encontrar una respuesta aceptable para mi situación. Básicamente, Java no tiene un codificador incorporado para UTF-16LE con una lista de materiales. Y así, usted tiene que lanzar su propia aplicación.

Esto es lo que terminé con:

private byte[] encodeUTF16LEWithBOM(final String s) {
    ByteBuffer content = Charset.forName("UTF-16LE").encode(s);
    byte[] bom = { (byte) 0xff, (byte) 0xfe };
    return ByteBuffer.allocate(content.capacity() + bom.length).put(bom).put(content).array();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top