W języku Java Jak przekonwertować tablicę bajtów na ciąg cyfr szesnastkowych, zachowując początkowe zera?

Pracuję nad przykładem kodu Javy do tworzenia skrótów md5. Jedna część zamienia wyniki z bajtów na ciąg cyfr szesnastkowych:

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

Jednak to nie działa, ponieważ tohexstring najwyraźniej zrzuca wiodące zera. Więc, jaki jest najprostszy sposób przejścia z tablicy bajtów do ciągu szesnastkowego, który utrzymuje początkowe zera?

 151
Author: Eugene M, 2008-12-01

27 answers

Proste podejście polegałoby na sprawdzeniu, ile cyfr jest wyprowadzanych przez Integer.toHexString() i dodaniu wiodącego zera do każdego bajtu w razie potrzeby. Coś takiego:

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();
}
 93
Author: Michael Myers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-27 01:30:39

Sprawdź kodek Apache Commons Hex.encodeHex . Typ zwracany jest char[], który można trywialnie przekształcić na String. Więc:

import org.apache.commons.codec.binary;

Hex.encodeHexString(messageDigest);
 123
Author: Brandon DuRette,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-19 17:30:55

Możesz użyć tego poniżej. Testowałem to z początkowymi zerowymi bajtami i z początkowymi ujemnymi bajtami

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

Jeśli chcesz mieć małe cyfry szesnastkowe, użyj "x" w łańcuchu formatu.

 105
Author: Ayman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-03 10:09:07

Użyj DatatypeConverter.printHexBinary(). Możesz przeczytać jego dokumentację w http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html

Na przykład:

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

Spowoduje:

000086003D
 36
Author: Gareth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-24 09:47:46

Podobały mi się wypowiedzi Steve ' a, ale mógł zrobić to bez kilku zmiennych i zapisać kilka linijek w procesie.

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

Podoba mi się to, że łatwo jest zobaczyć dokładnie, co robi (zamiast polegać na jakiejś magicznej konwersji BigInteger black box), a także jesteś wolny od martwienia się o sprawy narożne, takie jak zera prowadzące i takie tam. Ta procedura pobiera każde 4-bitowe skubanie i zamienia je w znak sześciokątny. I korzysta z przeszukiwania tabeli, więc prawdopodobnie szybko. Prawdopodobnie byłoby to szybsze, jeśli zastąpisz v / 16 I V%16 przesunięciami bitowymi i I, ale jestem zbyt leniwy, aby to teraz przetestować.

 31
Author: Jemenake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-04 06:03:37

Znalazłem liczbę całkowitą.toHexString być trochę powolny. Jeśli konwertujesz wiele bajtów, warto rozważyć zbudowanie tablicy łańcuchów zawierających "00".."FF" i użyj liczby całkowitej jako indeksu. I. E.

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

Jest to szybsze i zapewnia prawidłową długość. Po prostu wymaga tablicy łańcuchów:

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
Author: David Snabel-Caunt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-23 19:22:02

Szukałem tego samego ... kilka dobrych pomysłów tutaj, ale zrobiłem kilka mikro benchmarków. Okazało się, że najszybszy (zmodyfikowany z powyższego Aymana i około 2x tak szybki, i około 50% szybszy niż Steve ' a tuż nad tym): {]}

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-brakowało, że jest to zasadniczo to samo co kgiannakakis i tak może rozebrać wiodące 0. Mimo to, modyfikując to do następującej, nadal jest najszybsze:

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
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-26 04:15:55
static String toHex(byte[] digest) {
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%1$02X", b));
    }

    return sb.toString();
}
 11
Author: Illarion Kovalchuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-14 14:55:04
String result = String.format("%0" + messageDigest.length + "s", hexString.toString())
To najkrótsze rozwiązanie, biorąc pod uwagę to, co już masz. Jeśli można przekonwertować tablicę bajtów na wartość liczbową, String.format można przekonwertować ją na łańcuch szesnastkowy w tym samym czasie.
 6
Author: Ed Marty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-01 20:33:15

To rozwiązanie jest trochę starsze i powinno być wydajne pamięci.

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
Author: agentbillo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-02 15:21:12

Inna opcja

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
Author: Peter Lawrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-03 21:04:01

Aby zachować wiodące zera, oto mała odmiana tego, co zasugerował Paul (np. 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, to wygląda gorzej niż to, co proponuje Ayman, przepraszam za to

 4
Author: F.X,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-13 10:09:56
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
Author: max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-08 08:57:37

Wygląda na to, że funkcje concat i append mogą być bardzo wolne. Poniższy był dla mnie o wiele szybszy (niż mój poprzedni post). Zmiana na tablicę znaków podczas budowania wyjścia była kluczowym czynnikiem przyspieszającym. Nie porównałem do Hex.encodeHex zasugerował Brandon DuRette.

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
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 20:08:59

Guava sprawia, że to bardzo proste:

BaseEncoding.base16().encode( bytes );

To fajna alternatywa, gdy Apache Commons nie jest dostępne. Ma również kilka ładnych kontrolek wyjścia, takich jak:

byte[] bytes = new byte[] { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
BaseEncoding.base16().lowerCase().withSeparator( ":", 2 ).encode( bytes );
// "0a:0b:0c:0d:0e:0f"
 3
Author: kichik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-22 20:17:14

Użyłbym czegoś takiego dla stałej długości, jak hashes:

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

0 w masce robi wyściółkę...

 3
Author: Usagi Miyamoto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 07:26:43

Tego używam do hashów MD5:

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: testowałem i zauważyłem, że przy tym również końcowe zera są wycinane. Ale to może się zdarzyć tylko na początku, więc można porównać z oczekiwaną długość i podkładka odpowiednio.

 2
Author: kgiannakakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-01 20:42:30

Możesz uzyskać to pisząc mniej bez zewnętrznych bibliotek:

String hex = (new HexBinaryAdapter()).marshal(md5.digest(YOUR_STRING.getBytes()))
 2
Author: arutaku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-20 14:07:33

To rozwiązanie nie wymaga przesuwania bitów lub maskowania, tabel wyszukiwania lub zewnętrznych bibliotek i jest tak krótkie, jak tylko mogę uzyskać:

byte[] digest = new byte[16];       

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

fmt.toString()
 2
Author: bearontheroof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-21 00:16:59
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
Author: Fernando Miguélez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-01 20:41:14

IMHO wszystkie powyższe rozwiązania, które zapewniają urywki do usuwania wiodących zer, są błędne.

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

Zgodnie z tym fragmentem, 8 bitów jest pobieranych z tablicy bajtów w iteracja, przekształcona w liczbę całkowitą (od Integer.funkcja tohexstring zajmuje int jako argument), a następnie ta liczba całkowita jest konwertowana na odpowiedni hash wartość. Tak więc, na przykład, jeśli masz 00000001 00000001 w binarnym, zgodnie z kod, zmienna hexString będzie miała 0x11 jako wartość hex a także mając na uwadze poprawna wartość powinna wynosić 0x0101. Tak więc przy obliczaniu MD5 możemy otrzymać hashe o długości

Rozwiązaniem problemu jest zastąpienie powyższego fragmentu kodu przez następujący fragment:

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
Author: Divij,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-27 10:42:10

To da podwójny łańcuch znaków dla bajtu.

public String toString(byte b){
    final char[] Hex = new String("0123456789ABCDEF").toCharArray();
    return  "0x"+ Hex[(b & 0xF0) >> 4]+ Hex[(b & 0x0F)];
}
 0
Author: Hatto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-31 18:09:13

I jak można konwertować z powrotem z ascii do tablicy bajtów ?

Podążałem za poniższym kodem, aby przekonwertować do ascii podanego przez Jemenake.

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
Author: Dhimant Jayswal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-12 10:53:12

Mój wariant

    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());
U mnie działa.
 0
Author: wbr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-29 19:58:20

Czy to błędne rozwiązanie? (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;

Więc w zasadzie zastępuje spacje przez 0.

 0
Author: Stan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-24 08:07:50

Dziwi mnie, że nikt nie wymyślił następującego rozwiązania:

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
Author: halber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-17 20:34:31

Jest to również równoważne, ale bardziej zwięzłe przy użyciu Apache util HexBin gdzie kod zmniejsza się do

HexBin.encode(messageDigest).toLowerCase();
 -1
Author: neel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-27 07:58:37