Konvertieren von char [] in byte []

83

Ich möchte ein Zeichenarray in ein Bytearray in Java konvertieren. Welche Methoden gibt es für diese Konvertierung?

Arun Abraham
quelle

Antworten:

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

oder

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

um einen nicht standardmäßigen Zeichensatz zu erhalten.

Update: Seit Java 7:new String(ch).getBytes(StandardCharsets.UTF_8);

Tarlog
quelle
4
Die Verwendung des Standardzeichensatzes der Plattform ist meistens falsch (Web-Apps).
Maaartinus
4
Dies ist eine triviale Lösung, da durch die Verwendung eines neuen Strings der für die Operation benötigte Speicherplatz verdoppelt wird. Es funktioniert nicht sehr gut für extrem große Eingänge.
Levent Divilioglu
163

Konvertieren ohne StringObjekt zu erstellen :

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

Verwendung:

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

Die Lösung basiert auf der Empfehlung von Swing, Passwörter in char [] zu speichern. (Siehe Warum wird char [] für Passwörter gegenüber String bevorzugt? )

Denken Sie daran, keine vertraulichen Daten in Protokolle zu schreiben und sicherzustellen, dass JVM keine Verweise darauf enthält.


Der obige Code ist korrekt, aber nicht wirksam. Wenn Sie keine Leistung benötigen, aber Sicherheit wünschen, können Sie diese verwenden. Wenn Sicherheit auch kein Ziel ist, dann einfach tun String.getBytes. Der obige Code ist nicht wirksam, wenn Sie sich die Implementierung encodein JDK ansehen . Außerdem müssen Sie Arrays kopieren und Puffer erstellen. Eine andere Möglichkeit zum Konvertieren besteht darin, den gesamten Code inline zu integrieren encode(Beispiel für 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"))

Entschuldigen Sie, dass ich die Scala-Sprache verwende. Wenn Sie Probleme beim Konvertieren dieses Codes in Java haben, kann ich ihn neu schreiben. Was ist mit der Leistung? Überprüfen Sie immer die realen Daten (z. B. mit JMH). Dieser Code sieht dem in JDK [ 2 ] und Protobuf [ 3 ] sehr ähnlich .

Andrii Nemchenko
quelle
Würde dies nicht einen ByteBuffer erstellen? Ich denke, das ist weniger teuer als ein String-Objekt?
Andi Jay
15
@CrazyJay Ich glaube, diese Methode würde keine "Zeichen" im String Pool speichern. Auf diese Weise können Sie sicherer mit Kennwortdaten arbeiten.
Andrii Nemchenko
1
@Cassian Ihre Methode funktioniert nicht richtig. Lesen Sie Details hier stackoverflow.com/a/20604909/355491
Andrii Nemchenko
1
@Prabs Nein, ein UTF-8-Zeichen benötigt 1 bis 4 Byte. Sogar ein ASCII-Zeichen benötigt 8 Bits.
Andrii Nemchenko
1
Diese 'toBytes ()' - Methode hat einen wichtigen Nebeneffekt. Es löscht die Eingabezeichen. charBuffer.array () ist eigentlich das Eingabezeichen. Arrays.fill () würde die Eingabe tatsächlich löschen. In vielen Fällen ist es in Ordnung, aber manchmal erzeugt es unerwünschte Effekte.
Guangliang
19

Bearbeiten: Andreys Antwort wurde aktualisiert, sodass Folgendes nicht mehr gilt.

Andreys Antwort (die höchste, die zum Zeitpunkt des Schreibens gewählt wurde) ist leicht falsch. Ich hätte dies als Kommentar hinzugefügt, aber ich bin nicht seriös genug.

In Andreys Antwort:

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

Der Aufruf von array () gibt möglicherweise nicht den gewünschten Wert zurück, zum Beispiel:

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

Ausgabe:

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

Wie zu sehen ist, wurde ein Null-Byte hinzugefügt. Um dies zu vermeiden, verwenden Sie Folgendes:

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

Ausgabe:

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

Da die Antwort auch auf die Verwendung von Passwörtern anspielte, kann es sinnvoll sein, das Array auszublenden, das den ByteBuffer unterstützt (Zugriff über die Funktion 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));
djsutho
quelle
Könnte das nachfolgende \ 0 implementierungsspezifisch sein? Ich verwende 1.7_51 mit Netbeans 7.4 und bemerke keine nachgestellten \ 0.
@orthopteroid ja dieses beispiel könnte jvm-spezifisch sein. Dies wurde mit Oracle 1.7.0_45 Linux 64 Bit (aus dem Speicher) ausgeführt. Bei der folgenden Implementierung ( grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… ) werden Fehler angezeigt , wenn averageBytesPerChar()etwas anderes als 1 zurückgegeben wird (ich erhalte 1.1). Aus Interesse, welches Betriebssystem / welchen Arch Sie verwenden, habe ich mit Oracle 1.7.0_51 und openjdk 1.7.0_51 doppelt überprüft und festgestellt, dass es mit 10 Zeichen defekt ist.
Djsutho
@ Andy keine Sorgen. Beachten Sie, dass buffer.array()in der toBytesFunktion noch überschrieben werden muss, derzeit nur die Kopie.
Djsutho
@Andrey Ich habe meine Antwort bearbeitet, um die Änderungen widerzuspiegeln.
Djsutho
@djsutho Heute ist meine Plattform Windows 7x64. Der Code kann leider nicht angezeigt werden. Ich verwende Code wie "System.arraycopy (str.getBytes (" UTF-8 "), 0, stor, 0, used);" jetzt.
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;
}
Matt
quelle
-5

Sie könnten eine Methode machen:

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

Hoffe das hilft

Java ist cool
quelle
4
Diese Antwort ist falsch, da char-Daten Unicode sind und daher bis zu 4 Bytes pro Zeichen vorhanden sein können (mehr sind möglich, aber im wirklichen Leben habe ich nur bis zu 4 gefunden). Nur ein Byte von jedem Zeichen zu nehmen, funktioniert nur für einen sehr begrenzten Zeichensatz. Bitte lesen Sie unter joelonsoftware.com/articles/Unicode.html "Das absolute Minimum, das jeder Softwareentwickler unbedingt über Unicode und Zeichensätze wissen muss (keine Ausreden!)" .
Ilane