java - bom - utf-16 vs utf-8
¿Cómo puedo codificar/decodificar matrices de bytes UTF-16LE con una lista de materiales? (5)
Así es como lo haces en nio:
return Charset.forName("UTF-16LE").encode(message)
.put(0, (byte) 0xFF)
.put(1, (byte) 0xFE)
.array();
Ciertamente, se supone que es más rápido, pero no sé cuántos arreglos se hacen bajo las coberturas, pero mi comprensión del punto de la API es que se supone que debe minimizar eso.
Necesito codificar / decodificar arrays de bytes UTF-16 hacia y desde java.lang.String
. Las matrices de bytes se me asignan con un marcador de orden de bytes (BOM) , y necesito codificar matrices de bytes con una lista de materiales.
Además, debido a que estoy tratando con un cliente / servidor de Microsoft, me gustaría emitir la codificación en little endian (junto con el LE BOM) para evitar cualquier malentendido. Me doy cuenta de que con el BOM debería funcionar big endian, pero no quiero nadar contra la corriente en el mundo de Windows.
Como ejemplo, aquí hay un método que codifica un java.lang.String
como UTF-16
en little 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 copiar la matriz de bytes completa en una nueva matriz de bytes que tiene dos bytes adicionales asignados al principio.
Lo mismo ocurre con la decodificación de una cadena de este tipo, pero eso es mucho más sencillo usando el constructor java.lang.String
:
public String(byte[] bytes,
int offset,
int length,
String charsetName)
El nombre del conjunto de caracteres "UTF-16" siempre se codificará con una lista de materiales y decodificará los datos usando endianness grande / pequeño, pero "UnicodeBig" y "UnicodeLittle" son útiles para codificar en un orden de bytes específico. Use UTF-16LE o UTF-16BE para no BOM: vea esta publicación para saber cómo usar "/ uFEFF" para manejar las BOM de forma manual. Consulte here denominación canónica de los nombres de las cadenas de caracteres o (preferiblemente) la clase de conjuntos de caracteres. También tenga en cuenta que solo un subconjunto limitado de codificaciones es absolutamente necesario para ser soportado.
En primer lugar, para descodificar puede usar el conjunto de caracteres "UTF-16"; que detecta automáticamente una lista de materiales inicial. Para codificar UTF-16BE, también puede usar el conjunto de caracteres "UTF-16", que escribirá una lista de materiales adecuada y luego generará material de big endian.
Para codificar a little endian con una lista de materiales, no creo que su código actual sea tan malo, incluso con la doble asignación (a menos que sus cadenas sean realmente monstruosas). Lo que podría querer hacer si no es tratar con una matriz de bytes, sino con un java.nio ByteBuffer, y usar la clase java.nio.charset.CharsetEncoder. (Que puede obtener de Charset.forName ("UTF-16LE"). NewEncoder ()).
Esta es una pregunta antigua, 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í, tienes que desplegar tu propia implementació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();
}
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();
EDITAR: Al releer su pregunta, veo que preferiría evitar la asignación de doble matriz por completo. Desafortunadamente, la API no te da eso, por lo que sé. (Hubo un método, pero está en desuso y no puede especificar la codificación con él).
Escribí lo anterior antes de ver tu comentario, creo que la respuesta para usar las clases de nio está en el camino correcto. Lo estaba viendo, pero no estoy lo suficientemente familiarizado con la API como para saber de antemano cómo se hace eso.