web-dev-qa-db-fra.com

Conversion de char [] en octet []

Je voudrais convertir un tableau de caractères en un tableau d'octets en Java. Quelles méthodes existent pour effectuer cette conversion?

68
Arun Abraham
char[] ch = ?
new String(ch).getBytes();

ou

new String(ch).getBytes("UTF-8");

pour obtenir un jeu de caractères autre que celui par défaut.

Mise à jour: Depuis Java 7: new String(ch).getBytes(StandardCharsets.UTF_8);

68
Tarlog

Convertir sans créer l'objet String:

import Java.nio.CharBuffer;
import Java.nio.ByteBuffer;
import Java.util.Arrays;

byte[] toBytes(char[] chars) {
  CharBuffer charBuffer = CharBuffer.wrap(chars);
  ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
  byte[] bytes = Arrays.copyOfRange(byteBuffer.array(),
            byteBuffer.position(), byteBuffer.limit());
  Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
  return bytes;
}

Usage:

char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
byte[] bytes = toBytes(chars);
/* do something with chars/bytes */
Arrays.fill(chars, '\u0000'); // clear sensitive data
Arrays.fill(bytes, (byte) 0); // clear sensitive data

La solution s'inspire de la recommandation Swing pour stocker les mots de passe dans char []. (Voir Pourquoi char [] est-il préféré à String pour les mots de passe? )

N'oubliez pas de ne pas écrire de données sensibles dans les journaux et de vous assurer que JVM ne contiendra aucune référence à ces données.


Le code ci-dessus est correct mais pas efficace. Si vous n'avez pas besoin de performance mais que vous voulez de la sécurité, vous pouvez l'utiliser. Si la sécurité n’est pas non plus un objectif, faites simplement String.getBytes. Le code ci-dessus n'est pas efficace si vous négligez l'implémentation de encode dans JDK. En outre, vous devez copier des tableaux et créer des tampons. Un autre moyen de convertir le code en ligne derrière encode (exemple pour UTF-8)

val xs: Array[Char] = "A ß € 嗨 ???? ????".toArray
val len = xs.length
val ys: Array[Byte] = new Array(3 * len) // worst case
var i = 0; var j = 0 // i for chars; j for bytes
while (i < len) { // fill ys with bytes
  val c = xs(i)
  if (c < 0x80) {
    ys(j) = c.toByte
    i = i + 1
    j = j + 1
  } else if (c < 0x800) {
    ys(j) = (0xc0 | (c >> 6)).toByte
    ys(j + 1) = (0x80 | (c & 0x3f)).toByte
    i = i + 1
    j = j + 2
  } else if (Character.isHighSurrogate(c)) {
    if (len - i < 2) throw new Exception("overflow")
    val d = xs(i + 1)
    val uc: Int = 
      if (Character.isLowSurrogate(d)) {
        Character.toCodePoint(c, d)
      } else {
        throw new Exception("malformed")
      }
    ys(j) = (0xf0 | ((uc >> 18))).toByte
    ys(j + 1) = (0x80 | ((uc >> 12) & 0x3f)).toByte
    ys(j + 2) = (0x80 | ((uc >>  6) & 0x3f)).toByte
    ys(j + 3) = (0x80 | (uc & 0x3f)).toByte
    i = i + 2 // 2 chars
    j = j + 4
  } else if (Character.isLowSurrogate(c)) {
    throw new Exception("malformed")
  } else {
    ys(j) = (0xe0 | (c >> 12)).toByte
    ys(j + 1) = (0x80 | ((c >> 6) & 0x3f)).toByte
    ys(j + 2) = (0x80 | (c & 0x3f)).toByte
    i = i + 1
    j = j + 3
  }
}
// check
println(new String(ys, 0, j, "UTF-8"))

Excusez-moi d'utiliser le langage Scala. Si vous rencontrez des problèmes pour convertir ce code en Java, je peux le réécrire. Qu'en est-il des performances toujours vérifier sur des données réelles (avec JMH par exemple). Ce code est très similaire à ce que vous pouvez voir dans JDK [ 2 ] et Protobuf [ 3 ].

140
Nobody Tells

Edit: La réponse de Andrey a été mise à jour, de sorte que ce qui suit ne s'applique plus.

La réponse d'Andrey (le plus voté au moment de la rédaction) est légèrement incorrecte. J'aurais ajouté ceci comme commentaire mais je ne suis pas assez fiable.

Dans la réponse de Andreï:

char[] chars = {'c', 'h', 'a', 'r', 's'}
byte[] bytes = Charset.forName("UTF-8").encode(CharBuffer.wrap(chars)).array();

l'appel à array () peut ne pas renvoyer la valeur souhaitée, par exemple:

char[] c = "aaaaaaaaaa".toCharArray();
System.out.println(Arrays.toString(Charset.forName("UTF-8").encode(CharBuffer.wrap(c)).array()));

sortie:

[97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 0]

Comme on peut le constater, un octet zéro a été ajouté. Pour éviter cela, utilisez ce qui suit:

char[] c = "aaaaaaaaaa".toCharArray();
ByteBuffer bb = Charset.forName("UTF-8").encode(CharBuffer.wrap(c));
byte[] b = new byte[bb.remaining()];
bb.get(b);
System.out.println(Arrays.toString(b));

sortie:

[97, 97, 97, 97, 97, 97, 97, 97, 97, 97]

Comme la réponse faisait également allusion à l’utilisation de mots de passe, il pourrait être utile de supprimer le tableau qui sauvegarde le ByteBuffer (accessible via la fonction Array ()):

ByteBuffer bb = Charset.forName("UTF-8").encode(CharBuffer.wrap(c));
byte[] b = new byte[bb.remaining()];
bb.get(b);
blankOutByteArray(bb.array());
System.out.println(Arrays.toString(b));
17
djsutho
private static byte[] charArrayToByteArray(char[] c_array) {
        byte[] b_array = new byte[c_array.length];
        for(int i= 0; i < c_array.length; i++) {
            b_array[i] = (byte)(0xFF & (int)c_array[i]);
        }
        return b_array;
}
0
Matt