Comment encoder / décoder des tableaux D'octets UTF-16LE avec un BOM?

j'ai besoin d'encoder/décoder les réseaux de octets UTF-16 vers et à partir de java.lang.String . Les tableaux d'octets me sont donnés avec un marqueur d'ordre D'octets (BOM) , et j'ai besoin d'encoder des tableaux d'octets avec un BOM.

aussi, parce que J'ai affaire à un client/serveur Microsoft, j'aimerais émettre l'encodage en little endian (avec le le BOM) pour éviter tout malentendu. Je me rends compte qu'avec le BOM, ça devrait marcher pour big endian, mais je ne veux pas nager. en amont dans le monde Windows.

à titre d'exemple, voici une méthode qui code un java.lang.String comme UTF-16 dans little endian avec un BOM:

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;
}

Quelle est la meilleure façon de faire cela en Java? Idéalement, j'aimerais éviter de copier l'ensemble du tableau octet dans un nouveau tableau octet qui a deux octets supplémentaires alloués au début.

il en va de même pour le décodage d'une telle chaîne, mais c'est beaucoup plus simple en utilisant le java.lang.String constructeur :

public String(byte[] bytes,
              int offset,
              int length,
              String charsetName)
20
demandé sur Jared Oberhaus 2009-05-18 23:55:08

5 réponses

le nom du jeu de caractères "UTF-16" sera toujours encodé avec un BOM et décodera les données en utilisant soit un grand/petit endianness, mais "UnicodeBig" et "UnicodeLittle" sont utiles pour encoder dans un ordre d'octet spécifique. Utilisez UTF-16LE ou UTF-16BE pour no bom - voir ce post pour savoir comment utiliser" \uFEFF " pour manipuler des Bom à la main. Voir ici pour la dénomination canonique des noms de chaîne de caractères ou (de préférence) la classe Charset . Prenez aussi note que seul un sous-ensemble limité de codages sont absolument nécessaires pour être pris en charge.

27
répondu McDowell 2009-05-18 20:08:45

C'est comment vous le faites dans nio:

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

Il est certainement censé être plus rapide, mais je ne sais pas combien de tableaux qu'il fait sous le couvre, mais ma compréhension de la pointe de l'API est qu'il est censé minimiser cela.

7
répondu Yishai 2009-05-18 23:09:56

tout d'abord, pour le décodage vous pouvez utiliser le jeu de caractères "UTF-16"; qui détecte automatiquement un BOM initial. Pour encoder UTF-16BE, vous pouvez également utiliser le jeu de caractères "UTF-16" - qui écrira un bon BOM et produira ensuite de gros trucs endian.

pour encoder à little endian avec un BOM, Je ne pense pas que votre code actuel soit trop mauvais, même avec la double allocation (à moins que vos cordes soient vraiment monstrueuses). Ce que vous pourriez vouloir faire si elles sont ne traite pas avec un byte array mais plutôt un java.nio ByteBuffer, et d'utiliser le java.nio.jeu de caractères.Classe CharsetEncoder. (Que vous pouvez obtenir de Charset.forName ("UTF-16LE").newEncoder()).

6
répondu Daniel Martin 2009-05-18 20:15:47
    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: relisez votre question, je vois que vous préféreriez éviter l'allocation de double tableau tout à fait. Malheureusement, l'API ne vous donne pas ça, autant que je sache. (Il y avait une méthode, mais c'est déconseillé, et vous ne pouvez pas spécifier l'encodage).

j'ai écrit ce qui précède avant de voir votre commentaire, je pense que la réponse à utiliser les classes nio est sur la bonne voie. Je regardais ça, mais je ne suis pas assez familier avec l'API de savoir comment vous obtenez ce que fait.

2
répondu Yishai 2009-05-18 20:36:41

C'est une vieille question, mais pourtant, je n'ai pas pu trouver une réponse acceptable pour ma situation. En gros, Java n'a pas d'encodeur intégré pour UTF-16LE avec un BOM. Et donc, vous devez déployer votre propre implémentation.

voilà ce que j'ai fini par dire:

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();
}
0
répondu hopia 2017-08-24 22:17:10