Conversione di char [] in byte []


84

Vorrei convertire un array di caratteri in un array di byte in Java. Quali metodi esistono per effettuare questa conversione?

Risposte:


76
char[] ch = ?
new String(ch).getBytes();

o

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

per ottenere un set di caratteri non predefinito.

Aggiornamento: da Java 7:new String(ch).getBytes(StandardCharsets.UTF_8);


4
L'utilizzo del set di caratteri predefinito della piattaforma è spesso sbagliato (app Web).
maaartinus

4
Questa è una soluzione banale, poiché utilizzando una nuova String, lo spazio necessario per l'operazione viene raddoppiato. Non funzionerà molto bene per input estremamente grandi.
Levent Divilioglu

167

Converti senza creare Stringoggetti:

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

Utilizzo:

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 soluzione si ispira alla raccomandazione di Swing di memorizzare le password in char []. (Vedi Perché char [] è preferito a String per le password? )

Ricorda di non scrivere dati sensibili nei log e assicurati che JVM non conservi alcun riferimento ad essi.


Il codice sopra è corretto ma non efficace. Se non hai bisogno di prestazioni ma desideri sicurezza, puoi usarlo. Se anche la sicurezza non è un obiettivo, fallo semplicemente String.getBytes. Il codice sopra non è efficace se guardi verso il basso l'implementazione di encodein JDK. Inoltre è necessario copiare array e creare buffer. Un altro modo per convertire è inline tutto il codice sottostante encode(esempio per 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"))

Mi scusi se uso il linguaggio Scala. Se hai problemi con la conversione di questo codice in Java, posso riscriverlo. Per quanto riguarda le prestazioni, controlla sempre i dati reali (con JMH per esempio). Questo codice è molto simile a quello che puoi vedere in JDK [ 2 ] e Protobuf [ 3 ].


Questo non creerebbe un ByteBuffer? Immagino che sia meno costoso di un oggetto String?
Andi Jay

15
@CrazyJay Credo che questo metodo non memorizzerebbe "chars" in String Pool. In questo modo puoi lavorare con i dati delle password in modo più sicuro.
Andrii Nemchenko

1
@Cassian Il tuo metodo non funziona correttamente. Leggi dettagli qui stackoverflow.com/a/20604909/355491
Andrii Nemchenko

1
@Prabs No, un carattere UTF-8 richiede da 1 a 4 byte. Anche un carattere ASCII richiede 8 bit.
Andrii Nemchenko

1
Questo metodo "toBytes ()" ha un importante effetto collaterale. Cancella i caratteri di input. charBuffer.array () sono effettivamente i caratteri di input. Arrays.fill () cancellerebbe effettivamente l'input. In molti casi va bene, ma a volte crea effetti indesiderati.
Guangliang

19

Modifica: la risposta di Andrey è stata aggiornata, quindi quanto segue non si applica più.

La risposta di Andrey (il voto più alto al momento della scrittura) è leggermente errata. Avrei aggiunto questo come commento ma non sono abbastanza rispettabile.

Nella risposta di Andrey:

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

la chiamata ad array () potrebbe non restituire il valore desiderato, ad esempio:

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

produzione:

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

Come si può vedere è stato aggiunto uno zero byte. Per evitare ciò, utilizzare quanto segue:

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

produzione:

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

Poiché la risposta alludeva anche all'uso delle password, potrebbe valere la pena cancellare l'array che supporta ByteBuffer (accessibile tramite la funzione 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));

Il trailing \ 0 potrebbe essere specifico dell'implementazione? Sto usando 1.7_51 con netbeans 7.4 e non noto alcun trailing \ 0.

@ortotteroide sì, questo esempio potrebbe essere specifico per jvm. Questo è stato eseguito con oracle 1.7.0_45 linux a 64 bit (dalla memoria). Con la seguente implementazione ( grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… ) otterrai errori se averageBytesPerChar()restituisce qualcosa di diverso da 1 (ottengo 1.1). Per interesse quale sistema operativo / arch stai usando, ho ricontrollato con Oracle 1.7.0_51 e openjdk 1.7.0_51 e l'ho trovato rotto con 10 caratteri.
djsutho

@Andrey non preoccuparti. Si noti che buffer.array()nella toBytesfunzione deve ancora essere sovrascritta, attualmente solo la copia è.
djsutho

@Andrey Ho modificato la mia risposta per riflettere i cambiamenti.
djsutho

@djsutho Oggi, la mia piattaforma è windows7x64. Spiacenti, non posso mostrare il codice - Sto usando codice come "System.arraycopy (str.getBytes (" UTF-8 "), 0, stor, 0, used);" adesso.

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

-5

Potresti creare un metodo:

public byte[] toBytes(char[] data) {
byte[] toRet = new byte[data.length];
for(int i = 0; i < toRet.length; i++) {
toRet[i] = (byte) data[i];
}
return toRet;
}

Spero che sia di aiuto


4
Questa risposta non è corretta perché i dati char sono Unicode e come tali potrebbero esserci fino a 4 byte per carattere (più sono possibili, ma nella vita reale, ne ho trovati solo fino a 4). Prendendo semplicemente un byte da ogni carattere funzionerà solo per un set di caratteri molto limitato. Si prega di leggere "Il minimo assoluto che ogni sviluppatore di software deve assolutamente conoscere su Unicode e set di caratteri (nessuna scusa!)" Su joelonsoftware.com/articles/Unicode.html .
Ilane
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.