Question

Je dois encoder / décoder UTF-16 tableaux d'octets vers et à partir java.lang.String. Les tableaux d'octets me sont données avec un Byte Order Marker (BOM) , et j'ai besoin de tableaux d'octets codés avec une nomenclature.

En outre, parce que je traite avec un client Microsoft / serveur, je voudrais émettre l'encodage en peu endian (ainsi que la nomenclature LE) afin d'éviter tout malentendu. Je me rends compte que la nomenclature devrait travailler big endian, mais je ne veux pas nager en amont dans le monde Windows.

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

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 le faire en Java? Idéalement, je voudrais éviter de copier tout le tableau d'octets dans un nouveau tableau d'octets qui a deux octets supplémentaires alloués au début.

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

public String(byte[] bytes,
              int offset,
              int length,
              String charsetName)
Était-ce utile?

La solution

Le nom charset « UTF-16 » sera toujours encoder avec une nomenclature et décode les données en utilisant soit grand / petit boutisme, mais « UnicodeBig » et « UnicodeLittle » sont utiles pour l'encodage dans un ordre d'octet spécifique. Utilisez UTF-16LE ou UTF-16BE sans BOM - voir cette post pour savoir comment utiliser « \ uFEFF » pour gérer manuellement BOM. Voir pour la dénomination canonique de charset noms de chaîne ou (de préférence) Charset classe. Veuillez également prendre note que seul un sous-ensemble limité de encodages sont absolument nécessaires pour être pris en charge.

Autres conseils

Voici 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 il est sous les couvertures, mais ma compréhension du point de l'API est qu'il est censé minimiser cela.

Tout d'abord, pour vous décoder pouvez utiliser le jeu de caractères « UTF-16 »; qui détecte automatiquement une nomenclature initiale. Pour l'encodage UTF-16BE, vous pouvez également utiliser le jeu de caractères « UTF-16 » - qui écriront une nomenclature correcte puis sortie gros trucs endian

.

Pour le codage à peu endian avec une nomenclature, je ne pense pas que votre code actuel est trop mauvais, même avec la double allocation (à moins que vos chaînes sont vraiment monstrueux). Ce que vous pouvez faire si elles sont est traite pas un tableau d'octets, mais plutôt un java.nio ByteBuffer, et utiliser la classe java.nio.charset.CharsetEncoder. (Que vous pouvez obtenir à partir 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: En relisant votre question, je vois que vous préférez éviter l'allocation double tableau tout à fait. Malheureusement, l'API ne vous donne pas, pour autant que je sache. (Il y avait une méthode, mais il est dépréciée, et vous ne pouvez pas spécifier l'encodage avec elle).

J'ai écrit ci-dessus avant d'avoir vu votre commentaire, je pense que la réponse à utiliser les classes nio est sur la bonne voie. Je regardais, mais je ne suis pas assez familier avec l'API de savoir comment la main de vous obtenir ce fait.

Ceci est une question ancienne, mais encore, je ne pouvais pas trouver une réponse acceptable pour ma situation. Fondamentalement, Java ne dispose pas d'un encodeur intégré pour UTF-16LE avec une nomenclature. Et donc, vous devez déployer votre propre implémentation.

Voici ce que j'ai fini avec:

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();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top