wake-up-neo.com

Wie konvertiere ich in Java ein Byte-Array in eine Folge von Hex-Ziffern, wobei führende Nullen beibehalten werden?

Ich arbeite mit einem Beispiel-Java-Code für die Erstellung von MD5-Hashes. Ein Teil konvertiert die Ergebnisse von Bytes in eine Folge von Hex-Ziffern:

byte messageDigest[] = algorithm.digest();     
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
    hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
    }

Es funktioniert jedoch nicht ganz, da toHexString anscheinend führende Nullen absetzt. Was ist also der einfachste Weg, um von einem Byte-Array zu einem Hex-String zu gelangen, der die führenden Nullen beibehält?

156
Eugene M

Ein einfacher Ansatz wäre, zu prüfen, wie viele Ziffern von Integer.toHexString() ausgegeben werden, und bei Bedarf eine führende Null zu jedem Byte hinzuzufügen. Etwas wie das:

public static String toHexString(byte[] bytes) {
    StringBuilder hexString = new StringBuilder();

    for (int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(0xFF & bytes[i]);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }

    return hexString.toString();
}
95
Michael Myers

Check out Apache Commons Codec Hex.encodeHex . Der Rückgabetyp ist char[], der trivial in String konvertiert werden kann. So:

import org.Apache.commons.codec.binary;

Hex.encodeHexString(messageDigest);
124
Brandon DuRette

Sie können die folgende verwenden. Ich habe dies mit führenden Null-Bytes und auch mit anfänglichen negativen Bytes getestet

public static String toHex(byte[] bytes) {
    BigInteger bi = new BigInteger(1, bytes);
    return String.format("%0" + (bytes.length << 1) + "X", bi);
}

Wenn Sie Hexadezimalzeichen in Kleinbuchstaben verwenden möchten, verwenden Sie "x" im Format String.

106
Ayman

Verwenden Sie DatatypeConverter.printHexBinary(). Die Dokumentation finden Sie in http://docs.Oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html

Zum Beispiel:

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

Wird darin enden, dass:

000086003D
37
Gareth

Ich mochte die Beiträge von Steve, aber er hätte auf ein paar Variablen verzichten können und dabei mehrere Zeilen gespeichert.

public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for ( int j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[j*2] = hexArray[v/16];
        hexChars[j*2 + 1] = hexArray[v%16];
    }
    return new String(hexChars);
}

Was ich an diesem Spiel mag, ist, dass es einfach ist, genau zu erkennen, was es tut (anstatt sich auf eine magische BigInteger-Black-Box-Konvertierung zu verlassen), und Sie müssen sich auch nicht um Eckfälle wie führende Nullen und solche Sachen kümmern müssen. Diese Routine nimmt jedes 4-Bit-Nibble in ein Hex-Zeichen. Und es verwendet eine Tabellensuche, also ist es wahrscheinlich schnell. Es könnte wahrscheinlich schneller sein, wenn Sie v/16 und v% 16 durch bitweise Verschiebungen und UND ersetzen, aber ich bin zu faul, es jetzt zu testen.

32
Jemenake

Ich fand Integer.toHexString ein wenig langsam. Wenn Sie viele Bytes konvertieren, sollten Sie ein Array von Strings mit "00" .. "FF" erstellen und die Ganzzahl als Index verwenden. Das heißt.

hexString.append(hexArray[0xFF & messageDigest[i]]);

Dies ist schneller und sorgt für die richtige Länge. Benötigt nur das Array von Strings:

String[] hexArray = {
"00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
"10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
"20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
"30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
"40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
"50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
"60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
"70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
"80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
"90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
"A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
"B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
"C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
"D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
"E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
"F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
21
Steve

Ich habe das gleiche gesucht ... ein paar gute Ideen, aber ich habe ein paar Mikro-Benchmarks durchgeführt. Ich fand das Folgende am schnellsten (modifiziert von Aymans oben und etwa 2x so schnell und etwa 50% schneller als Steve direkt über diesem):

public static String hash(String text, String algorithm)
        throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
    return new BigInteger(1, hash).toString(16);
}

Edit: Oops - hat verpasst, dass dies im Wesentlichen das gleiche ist wie das von kgiannakakis, und so kann es sein, dass eine führende 0 abgezogen wird. Wenn Sie dies jedoch folgendermaßen ändern, ist es immer noch das schnellste:

public static String hash(String text, String algorithm)
        throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
    BigInteger bi = new BigInteger(1, hash);
    String result = bi.toString(16);
    if (result.length() % 2 != 0) {
        return "0" + result;
    }
    return result;
}
13
Paul
static String toHex(byte[] digest) {
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%1$02X", b));
    }

    return sb.toString();
}
11
String result = String.format("%0" + messageDigest.length + "s", hexString.toString())

Das ist die kürzeste Lösung für das, was Sie bereits haben. Wenn Sie das Byte-Array in einen numerischen Wert konvertieren könnten, kann String.format es gleichzeitig in einen Hex-String konvertieren.

6
Ed Marty

Andere Option

public static String toHexString(byte[]bytes) {
    StringBuilder sb = new StringBuilder(bytes.length*2);
    for(byte b: bytes)
      sb.append(Integer.toHexString(b+0x800).substring(1));
    return sb.toString();
}
5
Peter Lawrey

Ich würde so etwas für feste Länge verwenden, wie Hashes:

md5sum = String.format("%032x", new BigInteger(1, md.digest()));

Der 0 in der Maske füllt das Auffüllen ...

5
Usagi Miyamoto

Diese Lösung ist etwas älter und sollte speichereffizient sein.

public static String toHexString(byte bytes[]) {
    if (bytes == null) {
        return null;
    }

    StringBuffer sb = new StringBuffer();
    for (int iter = 0; iter < bytes.length; iter++) {
        byte high = (byte) ( (bytes[iter] & 0xf0) >> 4);
        byte low =  (byte)   (bytes[iter] & 0x0f);
        sb.append(nibble2char(high));
        sb.append(nibble2char(low));
    }

    return sb.toString();
}

private static char nibble2char(byte b) {
    byte nibble = (byte) (b & 0x0f);
    if (nibble < 10) {
        return (char) ('0' + nibble);
    }
    return (char) ('a' + nibble - 10);
}
5
agentbillo

Guave macht es auch ganz einfach:

BaseEncoding.base16().encode( bytes );

Es ist eine schöne Alternative, wenn Apache Commons nicht verfügbar ist. Es hat auch einige Steuerelemente von Nice wie:

byte[] bytes = new byte[] { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
BaseEncoding.base16().lowerCase().withSeparator( ":", 2 ).encode( bytes );
// "0a:0b:0c:0d:0e:0f"
4
kichik
static String toHex(byte[] digest) {
    String digits = "0123456789abcdef";
    StringBuilder sb = new StringBuilder(digest.length * 2);
    for (byte b : digest) {
        int bi = b & 0xff;
        sb.append(digits.charAt(bi >> 4));
        sb.append(digits.charAt(bi & 0xf));
    }
    return sb.toString();
}
4
max

Um führende Nullen beizubehalten, ist hier eine kleine Variation dessen, was Paul vorgeschlagen hat (zB md5-Hash):

public static String MD5hash(String text) throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance("MD5").digest(text.getBytes());
    return String.format("%032x",new BigInteger(1, hash));
}

Ups, das sieht ärmer aus als das, was Ayman vorgeschlagen hat, tut mir leid

4
F.X

Es scheint, Concat und Append-Funktionen können sehr langsam sein. Das Folgende war viel schneller für mich (als mein vorheriger Beitrag). Der Wechsel zu einem Char-Array beim Erstellen der Ausgabe war der Schlüsselfaktor für die Beschleunigung. Ich habe nicht mit Hex.encodeHex von Brandon DuRette verglichen.

public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[10000000];
    int c = 0;
    int v;
    for ( j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[c] = hexArray[v/16];
        c++;
        hexChars[c] = hexArray[v%16];
        c++;
    }
    return new String(hexChars, 0, c); }
3
Steve

Was ich für MD5-Hashes verwende:

public static String getMD5(String filename)
        throws NoSuchAlgorithmException, IOException {
    MessageDigest messageDigest = 
        Java.security.MessageDigest.getInstance("MD5");

    InputStream in = new FileInputStream(filename);

    byte [] buffer = new byte[8192];
    int len = in.read(buffer, 0, buffer.length);

    while (len > 0) {
        messageDigest.update(buffer, 0, len);
        len = in.read(buffer, 0, buffer.length);
    }
    in.close();

    return new BigInteger(1, messageDigest.digest()).toString(16);
}

EDIT: Ich habe getestet und mir ist aufgefallen, dass hiermit auch nachfolgende Nullen geschnitten werden. Dies kann jedoch nur am Anfang geschehen, sodass Sie mit der erwarteten Länge und dem entsprechenden Pad vergleichen können.

2
kgiannakakis

Ohne externe Bibliotheken können Sie weniger schreiben:

String hex = (new HexBinaryAdapter()).marshal(md5.digest(YOUR_STRING.getBytes()))
2
arutaku

Diese Lösung erfordert keine Bit-Shifting- oder -masking-Funktion, Suchtabellen oder externe Bibliotheken.

byte[] digest = new byte[16];       

Formatter fmt = new Formatter();    
for (byte b : digest) { 
  fmt.format("%02X", b);    
}

fmt.toString()
2
bearontheroof
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
    String hexByte = Integer.toHexString(0xFF & messageDigest[i]);
    int numDigits = 2 - hexByte.length();
    while (numDigits-- > 0) {
        hexString.append('0');
    }
    hexString.append(hexByte);
}
1

Ist das eine fehlerhafte Lösung? (Android Java)

    // Create MD5 Hash
    MessageDigest digest = Java.security.MessageDigest.getInstance("MD5");
    digest.update(s.getBytes());
    byte[] md5sum = digest.digest();
    BigInteger bigInt = new BigInteger(1, md5sum);
    String stringMD5 = bigInt.toString(16);
    // Fill to 32 chars
    stringMD5 = String.format("%32s", stringMD5).replace(' ', '0');
    return stringMD5;

Im Grunde ersetzt es also Leerzeichen durch 0.

0
Stan

meine Variante

    StringBuilder builder = new StringBuilder();
    for (byte b : bytes)
    {
        builder.append(Character.forDigit(b/16, 16));
        builder.append(Character.forDigit(b % 16, 16));
    }
    System.out.println(builder.toString());

für mich geht das.

0
wbr

IMHO sind alle obigen Lösungen, die Schnipsel zum Entfernen der führenden Nullen enthalten, falsch.

byte messageDigest[] = algorithm.digest();
for (int i = 0; i < messageDigest.length; i++) {
    hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}    

Gemäß diesem Snippet werden 8 Bits aus dem Byte-Array in einer -Iteration entnommen, in eine Ganzzahl konvertiert (da die Integer.toHexString-Funktionint als Argument verwendet), und diese Ganzzahl wird in den entsprechenden Hash .__ konvertiert. .Wert. Wenn Sie zum Beispiel 00000001 00000001 im Binärmodus haben, würde die hexString-Variable gemäß Den Code 0x11 als Hexadezimalwert haben, wohingegen der richtige Wert 0x0101 sein sollte. Bei der Berechnung von MD5 erhalten wir daher Hashes .__ mit einer Länge von <32 Byte (wegen fehlender Nullen), die möglicherweise nicht die kryptografisch eindeutigen Eigenschaften von MD5 erfüllen, die der MD5-Hash ausführt. 

Die Lösung des Problems ist das Ersetzen des obigen Code-Snippets durch das Folgende Snippet:

byte messageDigest[] = algorithm.digest();
for (int i = 0; i < messageDigest.length; i++) {
    int temp=0xFF & messageDigest[i];
    String s=Integer.toHexString(temp);
    if(temp<=0x0F){
        s="0"+s;
    }
    hexString.append(s);
}
0
Divij

Ich bin überrascht, dass niemand die folgende Lösung gefunden hat:

StringWriter sw = new StringWriter();
com.Sun.corba.se.impl.orbutil.HexOutputStream hex = new com.Sun.corba.se.impl.orbutil.HexOutputStream(sw);
hex.write(byteArray);
System.out.println(sw.toString());
0
halber

Und wie kann man wieder von ASCII zu Byte Array konvertieren?

ich folgte dem folgenden Code, um das von Jemenake angegebene ASCII zu konvertieren. 

public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for ( int j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[j*2] = hexArray[v/16];
        hexChars[j*2 + 1] = hexArray[v%16];
    }
    return new String(hexChars);
}
0
Dhimant Jayswal

Oder Sie können das tun:

byte[] digest = algorithm.digest();
StringBuilder byteContet = new StringBuilder();
for(byte b: digest){
 byteContent = String.format("%02x",b);
 byteContent.append(byteContent);
}

Es ist kurz, einfach und im Grunde nur ein Formatwechsel.

0
KumarAnkit

Dies gibt eine lange Zeichenfolge mit zwei Zeichen für ein Byte.

public String toString(byte b){
    final char[] Hex = new String("0123456789ABCDEF").toCharArray();
    return  "0x"+ Hex[(b & 0xF0) >> 4]+ Hex[(b & 0x0F)];
}
0
Hatto