En Java, comment convertir un tableau octet en chaîne de caractères hexadécimaux tout en gardant des zéros de tête?

je travaille avec un exemple de code java pour faire des hachures md5. Une partie convertit les résultats des octets en une chaîne de caractères hexadécimaux:

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

cependant, cela ne fonctionne pas tout à fait puisque toHexString semble tomber des zéros de tête. Alors, quelle est la façon la plus simple de passer d'un tableau octet à une chaîne hexadécimale qui maintient les zéros principaux?

151
demandé sur Eugene M 2008-12-01 23:24:43

27 réponses

une approche simple serait de vérifier combien de chiffres sont sortis par Integer.toHexString() et d'ajouter un zéro menant à chaque octet si nécessaire. Quelque chose comme ceci:

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
répondu Michael Myers 2016-02-27 01:30:39

Check out Apache Commons Codec Hex.encodeHex . Le type de retour est char[] , qui peut être trivialement converti en String . So:

import org.apache.commons.codec.binary;

Hex.encodeHexString(messageDigest);
123
répondu Brandon DuRette 2018-06-19 17:30:55

Vous pouvez utiliser celui-ci. J'ai testé avec zéro octets et négatif initial octets ainsi

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

si vous voulez des chiffres hexadécimaux minuscules, utilisez "x" dans la chaîne de format.

105
répondu Ayman 2009-06-03 10:09:07

utiliser DatatypeConverter.printHexBinary() . Vous pouvez lire sa documentation dans http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html

par exemple:

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

résultera en:

000086003D
36
répondu Gareth 2014-02-24 09:47:46

J'ai aimé les soumissions de Steve, mais il aurait pu le faire sans quelques variables et a enregistré plusieurs lignes dans le processus.

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

ce que j'aime à ce sujet est qu'il est facile de voir exactement ce qu'il fait (au lieu de compter sur une certaine conversion BigInteger boîte noire magique) et vous êtes également libre d'avoir à se soucier des cas de coin comme des zéros de tête et des trucs. Cette routine prend chaque grignotage 4-bit et le transforme en un hexagone. Et c'est à l'aide d'un recherche sur la table, donc c'est probablement rapide. Il pourrait probablement être plus rapide si vous remplacez v / 16 et v%16 par des déplacements bitwise et ET, mais je suis trop paresseux pour le tester maintenant.

31
répondu Jemenake 2010-02-04 06:03:37

j'ai trouvé entier.toHexString à être un peu lent. Si vous convertissez beaucoup d'octets, vous pouvez envisager de construire un tableau de chaînes contenant "00".."FF" et utiliser L'entier comme index. C'est-à-dire:

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

c'est plus rapide et assure la longueur correcte. Nécessite juste le tableau de cordes:

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
répondu David Snabel-Caunt 2011-08-23 19:22:02

je cherchais la même chose ... quelques bonnes idées ici, mais j'ai fait quelques micro-benchmarks. J'ai trouvé que ce qui suit est le plus rapide (modifié de Ayman ci-dessus et environ 2x aussi rapide, et environ 50% plus rapide que Steve juste au-dessus de celui-ci):

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: Oups manquer que ce sont essentiellement les mêmes que kgiannakakis et donc peut enlever un 0. Tout de même, en modifiant ceci en ce qui suit, c'est toujours le plus rapide:

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
répondu 2009-06-26 04:15:55
static String toHex(byte[] digest) {
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%1X", b));
    }

    return sb.toString();
}
11
répondu Illarion Kovalchuk 2010-04-14 14:55:04
String result = String.format("%0" + messageDigest.length + "s", hexString.toString())

c'est la solution la plus courte compte tenu de ce que vous avez déjà. Si vous pouvez convertir le tableau d'octets en une valeur numérique, String.format pouvez la convertir en une chaîne hexadécimale en même temps.

6
répondu Ed Marty 2008-12-01 20:33:15

cette solution est un peu plus vieille école, et devrait être efficace de mémoire.

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
répondu agentbillo 2008-12-02 15:21:12

une autre 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
répondu Peter Lawrey 2009-06-03 21:04:01

afin de garder les zéros de tête, voici une petite variation sur ce que Paul a suggéré (par exemple le hash md5):

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

Oups, ça a l'air plus pauvre que ce Qu'Ayman a proposé, désolé pour ça

4
répondu F.X 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
répondu max 2010-10-08 08:57:37

il semble concat et ajoute fonctions peuvent être vraiment lents. La suite a été BEAUCOUP plus rapide pour moi (de mon post précédent). Changer de char tableau dans la construction de la sortie a été le facteur clé pour accélérer. Je n'ai pas comparé à l'Hex.encodeHex suggéré par 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
répondu 2009-06-29 20:08:59

Guava c'est assez simple aussi:

BaseEncoding.base16().encode( bytes );

c'est une bonne alternative quand Apache Commons n'est pas disponible. Il a également quelques bons contrôles de la sortie comme:

byte[] bytes = new byte[] { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
BaseEncoding.base16().lowerCase().withSeparator( ":", 2 ).encode( bytes );
// "0a:0b:0c:0d:0e:0f"
3
répondu kichik 2015-01-22 20:17:14

j'utiliserais quelque chose comme ça pour une longueur fixe, comme des hachures:

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

le 0 dans le masque fait le rembourrage...

3
répondu Usagi Miyamoto 2017-05-22 07:26:43

ce que j'utilise pour les hachures 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: j'ai testé et j'ai remarqué qu'avec cela aussi les zéros sont coupés. Mais cela ne peut se produire qu'au début, de sorte que vous pouvez comparer avec la longueur attendue et pad en conséquence.

2
répondu kgiannakakis 2008-12-01 20:42:30

Vous pouvez l'obtenir par écrit moins sans les bibliothèques externes:

String hex = (new HexBinaryAdapter()).marshal(md5.digest(YOUR_STRING.getBytes()))
2
répondu arutaku 2012-09-20 14:07:33

Cette solution ne nécessite aucun changement de bit ou masquage, tables de recherche, ou bibliothèques externes, et est à peu près aussi court que je peux obtenir:

byte[] digest = new byte[16];       

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

fmt.toString()
2
répondu bearontheroof 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
répondu Fernando Miguélez 2008-12-01 20:41:14

IMHO toutes les solutions ci-dessus qui fournissent des snippets pour supprimer les zéros de tête sont fausses.

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

selon cet extrait, 8 bits sont pris dans le tableau des octets dans un itération, convertie en un entier (puisque entier.la fonction toHexString prend int comme argument) et puis cet entier est converti en hash correspondant valeur. Ainsi, par exemple, si vous avez 00000001 00000001 en binaire, selon le code, la variable hexString aurait 0x11 comme valeur hexadécimale alors que la valeur correcte devrait être 0x0101. Ainsi, tout en calculant MD5 nous pouvons obtenir des hachures de longueur <32 octets (en raison de zéros manquants) qui peuvent ne pas satisfaire propriétés cryptographiques uniques que possède le hash MD5.

la solution au problème est de remplacer l'extrait de code ci-dessus par le extrait suivant:

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
répondu Divij 2012-01-27 10:42:10

Cela donnera deux-char long chaîne pour un octet.

public String toString(byte b){
    final char[] Hex = new String("0123456789ABCDEF").toCharArray();
    return  "0x"+ Hex[(b & 0xF0) >> 4]+ Hex[(b & 0x0F)];
}
0
répondu Hatto 2013-01-31 18:09:13

et comment pouvez-vous convertir de nouveau d'ascii en byte array ?

j'ai suivi le code suivant pour convertir en ascii donné par 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
répondu Dhimant Jayswal 2013-06-12 10:53:12

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

ça me va.

0
répondu wbr 2013-08-29 19:58:20

est-ce une mauvaise solution? (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;

donc en gros il remplace les espaces par 0.

0
répondu Stan 2014-04-24 08:07:50

je suis surpris que personne n'ait trouvé la solution suivante:

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
répondu halber 2016-02-17 20:34:31

c'est aussi équivalent mais plus concis en utilisant Apache util HexBin où le code se réduit à

HexBin.encode(messageDigest).toLowerCase();
-1
répondu neel 2012-09-27 07:58:37