828 Stimmen

Wie konvertiert man in Java ein Byte-Array in einen Hex-String?

Ich habe ein Byte-Array mit Hexadezimalzahlen gefüllt und drucken Sie es der einfache Weg ist ziemlich sinnlos, weil es viele nicht druckbare Elemente. Was ich brauche, ist der genaue Hexcode in Form von: 3a5f771c

1075voto

maybeWeCouldStealAVan Punkte 14919

Aus der Diskussion aquí und insbesondere este Antwort, dies ist die Funktion, die ich derzeit verwende:

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

Meine eigenen kleinen Benchmarks (eine Million Bytes tausendmal, 256 Bytes 10 Millionen mal) haben gezeigt, dass es viel schneller als jede andere Alternative ist, etwa die Hälfte der Zeit bei langen Arrays. Verglichen mit der Antwort, aus der ich sie entnommen habe, hat der Wechsel zu bitweisen Operationen - wie in der Diskussion vorgeschlagen - die Zeit für lange Arrays um etwa 20% reduziert. (Edit: Wenn ich sage, dass es schneller als die Alternativen ist, meine ich den alternativen Code, der in der Diskussion angeboten wird. Die Leistung entspricht der des Commons Codec, der einen sehr ähnlichen Code verwendet).

2k20 Version, in Bezug auf Java 9 compact strings:

private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static String bytesToHex(byte[] bytes) {
    byte[] hexChars = new byte[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars, StandardCharsets.UTF_8);
}

542voto

chooban Punkte 8576

En Apache Commons Codec Bibliothek hat eine Hex Klasse für genau diese Art von Arbeit.

import org.apache.commons.codec.binary.Hex;

String foo = "I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );

354voto

PhoneixS Punkte 9789

Die Methode javax.xml.bind.DatatypeConverter.printHexBinary() , Teil des Java-Architektur für XML-Bindung (JAXB) war eine bequeme Möglichkeit zur Umwandlung einer byte[] in eine Hex-Zeichenkette. Die DatatypeConverter Klasse enthielt auch viele andere nützliche Methoden zur Datenmanipulation.

In Java 8 und früher war JAXB Teil der Java-Standardbibliothek. Sie war Abgelehnt mit Java 9 und entfernt mit Java 11 als Teil der Bemühungen, alle Java EE-Pakete in ihre eigenen Bibliotheken zu verschieben. Das ist eine lange Geschichte . Ahora, javax.xml.bind gibt es nicht, und wenn Sie JAXB verwenden wollen, das die DatatypeConverter installieren, müssen Sie die JAXB-API et JAXB-Laufzeit von Maven.

Beispiel für die Verwendung:

byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
String hex = javax.xml.bind.DatatypeConverter.printHexBinary(bytes);

Wird dazu führen:

000086003D

Diese Antwort ist die gleiche wie diese .

292voto

Pointer Null Punkte 38260

Einfachste Lösung, keine externen Bibliotheken, keine Ziffernkonstanten:

public static String byteArrayToHex(byte[] a) {
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a)
      sb.append(String.format("%02x", b));
   return sb.toString();
}

209voto

Patrick Favre Punkte 31611

Hier sind einige gängige Optionen, geordnet von einfach (Einzeiler) bis komplex (große Bibliothek). Wenn Sie an der Leistung interessiert sind, sehen Sie sich die Mikro-Benchmarks unten an.

Option 1: Codeschnipsel - Einfach (nur mit JDK/Android)

Option 1a: BigInteger

Eine sehr einfache Lösung ist die Verwendung der BigInteger die Hexadezimal-Darstellung:

new BigInteger(1, someByteArray).toString(16);

Beachten Sie, dass diese Handhabung Zahlen nicht willkürlich Byte-Zeichenketten werden führende Nullen weggelassen - dies kann, muss aber nicht das sein, was Sie wollen (z. B. 000AE3 vs 0AE3 für eine 3-Byte-Eingabe). Auch dies ist sehr langsam, etwa 100x langsamer im Vergleich zu Option 2.

Option 1b: String.format()

Die Verwendung des %X Platzhalter, String.format() ist in der Lage, die meisten primitiven Typen zu kodieren ( short , int , long ) in Hex:

String.format("%X", ByteBuffer.wrap(eightByteArray).getLong());

Option 1c: Integer/Long (nur 4/8-Byte-Arrays)

Wenn Sie ausschließlich 4-Byte-Arrays haben können Sie die toHexString Methode der Klasse Integer:

Integer.toHexString(ByteBuffer.wrap(fourByteArray).getInt());

Das Gleiche gilt für 8-Byte-Arrays et Long

Long.toHexString(ByteBuffer.wrap(eightByteArray).getLong());

Option 1d: JDK17+ HexFormat

Schließlich bietet JDK 17 auf der ersten Ebene Unterstützung für einfache Hex-Kodierung mit HexFormat :

HexFormat hex = HexFormat.of();
hex.formatHex(someByteArray)

Option 2: Codeschnipsel - Erweitert

Hier ist ein voll funktionsfähiger, kopier- und einfügbarer Codeschnipsel zur Unterstützung Groß-/Kleinschreibung et endianness . Es ist optimiert, um die Speicherkomplexität zu minimieren und die Leistung zu maximieren, und sollte mit allen modernen Java-Versionen (5+) kompatibel sein.

private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};

public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {

    // our output size will be exactly 2x byte-array length
    final char[] buffer = new char[byteArray.length * 2];

    // choose lower or uppercase lookup table
    final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;

    int index;
    for (int i = 0; i < byteArray.length; i++) {
        // for little endian we count from last to first
        index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;

        // extract the upper 4 bit and look up char (0-A)
        buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
        // extract the lower 4 bit and look up char (0-A)
        buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
    }
    return new String(buffer);
}

public static String encode(byte[] byteArray) {
    return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}

Der vollständige Quellcode mit Apache v2-Lizenz und Decoder ist zu finden unter aquí .

Option 3: Verwendung einer kleinen optimierten Bibliothek: bytes-java

Während der Arbeit an meinem letzten Projekt habe ich dieses kleine Toolkit für die Arbeit mit Bytes in Java erstellt. Es hat keine externen Abhängigkeiten und ist mit Java 7+ kompatibel. Es enthält unter anderem einen sehr schnellen und gut getesteten HEX-En/Decoder:

import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()

Sie können es überprüfen auf Github: bytes-java .

Option 4: Apache Commons Codec

Natürlich gibt es auch die gute, alte Gemeinsame Codecs . ( warnende Meinung voraus ) Während ich an dem oben beschriebenen Projekt arbeitete, analysierte ich den Code und war ziemlich enttäuscht; eine Menge doppelter, unorganisierter Code, veraltete und exotische Codecs, die wahrscheinlich nur für sehr wenige nützlich sind, und ziemlich überkonstruierte und langsame Implementierungen beliebter Codecs (insbesondere Base64). Ich würde daher eine informierte Entscheidung treffen, wenn Sie es oder eine Alternative verwenden möchten. Wie auch immer, wenn Sie es trotzdem benutzen wollen, hier ist ein Code-Schnipsel:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));

Option 5: Google Guava

In den meisten Fällen haben Sie bereits Guave als eine Abhängigkeit. Wenn ja, verwenden Sie einfach:

import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);

Option 6: Frühjahrssicherheit

Wenn Sie die Frühlingsrahmen avec Frühlingssicherheit können Sie Folgendes verwenden:

import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));

Option 7: Hüpfburg

Wenn Sie bereits den Sicherheitsrahmen verwenden Hüpfburg können Sie seine Hex nutzen:

import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);

Nicht wirklich Option 8: Java 9+ Kompatibilität oder 'Do Not Use JAXBs javax/xml/bind/DatatatypeConverter'

In früheren Java-Versionen (8 und darunter) war der Java-Code für JAXB als Laufzeitabhängigkeit enthalten. Seit Java 9 und Modularisierung der Puzzlesteine Ihr Code kann ohne ausdrückliche Erklärung nicht auf anderen Code außerhalb seines Moduls zugreifen. Seien Sie sich also bewusst, wenn Sie eine Ausnahme erhalten wie:

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

wenn es auf einer JVM mit Java 9+ läuft. Wenn dies der Fall ist, wechseln Sie zu einer der oben genannten Implementierungen. Siehe auch dies Frage .


Mikro-Benchmarks

Hier sind die Ergebnisse einer einfachen JMH Mikro-Benchmark-Codierung Byte-Arrays unterschiedlicher Größe . Die Werte sind Operationen pro Sekunde, also höher ist besser. Beachten Sie, dass Mikro-Benchmarks sehr oft nicht das Verhalten in der realen Welt widerspiegeln.

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger     |  2,088,514 |  1,008,357 |   133,665 |       4 |
| Opt2/3: Bytes Lib    | 20,423,170 | 16,049,841 | 6,685,522 |     825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 |     529 |
| Opt5: Guava          | 10,177,925 |  6,937,833 | 2,094,658 |     257 |
| Opt6: Spring         | 18,704,986 | 13,643,374 | 4,904,805 |     601 |
| Opt7: BC             |  7,501,666 |  3,674,422 | 1,077,236 |     152 |
| Opt8: JAX-B          | 13,497,736 |  8,312,834 | 2,590,940 |     346 |

Spezifikationen: JDK 8u202, i7-7700K, Win10, 24GB Ram. Siehe den vollständigen Benchmark aquí .

Benchmark-Update 2022

Hier sind die Ergebnisse mit dem aktuellen JMH 1.35, Java 17 und einem höherwertigen Computer

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger     |  2,941,403 |  1,389,448 |   242,096 |       5 |
| Opt2/3: Bytes Lib    | 31,724,981 | 22,786,906 | 6,197,028 |     930 |

Technische Daten: JDK temurin 17.0.4, Ryzen 5900X, Win11, 24GB DDR4 Ram

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X