web-dev-qa-db-fra.com

En Java, comment convertir un tableau d'octets en chaîne de chiffres hexadécimaux tout en conservant les zéros au début?

Je travaille avec un exemple de code Java pour la création de hachages md5. Une partie convertit les résultats d'octets en une chaîne de chiffres 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 avoir supprimé les zéros non significatifs. Alors, quel est le moyen le plus simple de passer d’un tableau d’octets à une chaîne hexagonale qui conserve les zéros de tête?

156
Eugene M

Une approche simple consisterait à vérifier le nombre de chiffres générés par Integer.toHexString() et à ajouter un zéro à chaque octet, si nécessaire. Quelque chose comme ça:

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

Découvrez Apache Commons Codec Hex.encodeHex . Le type de retour est char[], qui peut être facilement converti en String. Alors:

import org.Apache.commons.codec.binary;

Hex.encodeHexString(messageDigest);
124
Brandon DuRette

Vous pouvez utiliser celui ci-dessous. J'ai testé cela avec zéro octet et avec des octets négatifs initiaux, ainsi

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

Si vous souhaitez utiliser des chiffres hexadécimaux minuscules, utilisez "x" au format Chaîne.

106
Ayman

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

Aura pour résultat:

000086003D
37
Gareth

J'ai aimé les soumissions de Steve, mais il aurait pu se passer de quelques variables et sauvegarder 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 qui me plaît dans cette situation, c’est qu’il est facile de voir exactement ce que cela fait (au lieu de compter sur une conversion magique de la boîte noire BigInteger) et que vous n’avez plus à vous soucier des virages, comme les zéros non significatifs, etc. Cette routine prend chaque quartet de 4 bits et le transforme en caractère hexadécimal. Et comme il utilise une table de consultation, il est probablement rapide. Cela pourrait probablement être plus rapide si vous remplaciez v/16 et v% 16 par des décalages au niveau des bits et des AND, mais je suis trop paresseux pour le tester maintenant.

32
Jemenake

J'ai trouvé Integer.toHexString un peu lent. Si vous convertissez plusieurs 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]]);

Ceci est plus rapide et assure la bonne longueur. Nécessite juste le tableau de chaînes:

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

Je cherchais la même chose… de bonnes idées ici, mais j’ai exécuté quelques micro-points de repère. J'ai trouvé ce qui suit comme le plus rapide (modifié à partir de la version précédente d'Ayman et environ 2 fois plus rapide, et environ 50% plus rapide que celui de 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: Oops - a oublié qu'il s'agit essentiellement de la même chose que celle de kgiannakakis et peut donc effacer un 0 en tête. Néanmoins, en modifiant ceci, il reste 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
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
Illarion Kovalchuk
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 pouviez convertir le tableau d'octets en valeur numérique, String.format peut le convertir simultanément en chaîne hexadécimale.

6
Ed Marty

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
Peter Lawrey

Je voudrais utiliser quelque chose comme ceci pour une longueur fixe, comme des hachages:

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

Le 0 dans le masque fait le remplissage ...

5
Usagi Miyamoto

Cette solution est un peu ancienne école et devrait être efficace en 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
agentbillo

Goyave le rend assez simple aussi:

BaseEncoding.base16().encode( bytes );

C'est une alternative intéressante quand Apache Commons n'est pas disponible. Il a aussi quelques contrôles Nice 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"
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

Afin de conserver les zéros, voici une petite variation de ce que Paul a suggéré (par exemple, 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, cela semble plus pauvre que ce que Ayman a proposé, désolé pour cela

4
F.X

Il semble que les fonctions concat et append peuvent être très lentes. Ce qui suit a été BEAUCOUP plus rapide pour moi (que mon post précédent). Le passage à un tableau de caractères dans la construction de la sortie était le facteur clé pour l'accélérer. Je n'ai pas comparé à Hex.encodeHex proposé 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
Steve

C'est ce que j'utilise pour les hashes 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, les zéros de fin sont également coupés. Mais cela ne peut se produire qu'au début, vous pouvez donc comparer la longueur et le pad prévus en conséquence.

2
kgiannakakis

Vous pouvez l’obtenir en écrivant moins sans bibliothèques externes:

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

Cette solution ne nécessite ni décalage, ni masquage de bits, ni tables de consultation, ni bibliothèques externes.

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
Fernando Miguélez

Est-ce une solution défectueuse? (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, fondamentalement, il remplace les espaces par 0.

0
Stan

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 marche pour moi.

0
wbr

IMHO toutes les solutions ci-dessus qui fournissent des extraits pour supprimer les zéros sont faux.

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 extraits du tableau d'octets d'une itération Convertis en un entier (puisque la fonction Integer.toHexString prendint en argument), puis cet entier est converti en hachage correspondant .valeur. Ainsi, par exemple, si vous avez 00000001 00000001 en binaire, conformément au code , La variable hexString aurait pour valeur hexadécimale 0x11, tandis que la valeur correcte pour __ devrait être 0x0101. Ainsi, lors du calcul de MD5, nous pouvons obtenir des hachages De longueur <32 octets (en raison de zéros manquants) qui pourraient ne pas satisfaire aux propriétés cryptographiques uniques de MD5. 

La solution au problème consiste à remplacer l’extrait de code ci-dessus par l’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
Divij

Je suis surpris que personne n'ait proposé 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
halber

Et comment pouvez-vous reconvertir ascii en tableau d'octets?

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
Dhimant Jayswal

Ou vous pouvez faire ceci:

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

Son court, simple et fondamentalement juste un changement de format.

0
KumarAnkit

Cela donnera une chaîne longue de deux caractères 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
Hatto