Convertir une représentation de chaîne de caractères d'un dump hex en un tableau byte en utilisant Java?

je cherche un moyen de convertir une longue chaîne (à partir d'un dump), qui représente les valeurs hex en un tableau d'octets.

Je n'aurais pas pu le formuler mieux que la personne qui a affiché la même question ici .

mais pour le garder original, je vais le formuler à ma façon: supposons que j'ai une chaîne de caractères "00A0BF" que je voudrais interpréter comme le

byte[] {0x00,0xA0,0xBf}

que dois-je faire?

je suis novice en Java et j'ai fini par utiliser BigInteger et faire attention aux zéros hexadécimaux. Mais je pense qu'il est laid et je suis sûr que je manque quelque chose de simple.

304
demandé sur rafraf 2008-09-26 19:12:45

25 réponses

Voici une solution qui, à mon avis, est meilleure que toute autre publiée à ce jour:

public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

raisons pour lesquelles c'est une amélioration:

  • sûr avec des zéros de tête (contrairement à BigInteger) et avec des valeurs négatives de byte (contrairement à Byte.parseoctet)

  • ne convertit pas la chaîne en char[] , ni ne crée des objets StringBuilder et String pour chaque octet.

  • aucune dépendance de bibliothèque qui ne peut pas être disponible

N'hésitez pas à ajouter la vérification des arguments via assert ou des exceptions si l'argument n'est pas connu pour être sûr.

544
répondu Dave L. 2018-07-24 20:02:07

Monofiliers:

import javax.xml.bind.DatatypeConverter;

public static String toHexString(byte[] array) {
    return DatatypeConverter.printHexBinary(array);
}

public static byte[] toByteArray(String s) {
    return DatatypeConverter.parseHexBinary(s);
}

mises en garde :

  • en Java 9 Puzzle ce n'est plus une partie de l' (par défaut) de java.se racine défini de façon à ce Qu'il en résulte une ClassNotFoundException à moins que vous ne spécifiez -- add-modules java.se.ee (merci à @ eckes )
  • Non disponible sur Android( merci à Fabian pour noter que), mais vous pouvez juste prendre le code source si votre système manque javax.xml pour une raison quelconque. Merci à @ Bert Regelink pour avoir extrait la source.
307
répondu Vladislav Rastrusny 2017-05-23 11:33:26

la classe Hex de commons-codec devrait faire ça pour vous.

http://commons.apache.org/codec /

import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF
65
répondu skaffman 2018-09-27 20:27:37

vous pouvez maintenant utiliser BaseEncoding dans guava pour accomplir ceci.

BaseEncoding.base16().decode(string);

À l'inverse de

BaseEncoding.base16().encode(bytes);
32
répondu jontro 2018-02-01 15:51:27

en fait, je pense que la solution BigInteger est très agréable:

new BigInteger("00A0BF", 16).toByteArray();

Edit: Pas sûr pour les zéros , comme l'a noté l'affiche.

22
répondu Dave L. 2008-09-26 17:47:08

le HexBinaryAdapter fournit la capacité de marshal et unmarshal entre String et byte[] .

import javax.xml.bind.annotation.adapters.HexBinaryAdapter;

public byte[] hexToBytes(String hexString) {
     HexBinaryAdapter adapter = new HexBinaryAdapter();
     byte[] bytes = adapter.unmarshal(hexString);
     return bytes;
}

c'est juste un exemple que j'ai tapé...En fait, je l'utilise telle quelle et je n'ai pas besoin de faire une méthode séparée pour l'utiliser.

21
répondu GrkEngineer 2012-02-01 13:10:20

Monofiliers:

import javax.xml.bind.DatatypeConverter;

public static String toHexString(byte[] array) {
    return DatatypeConverter.printHexBinary(array);
}

public static byte[] toByteArray(String s) {
    return DatatypeConverter.parseHexBinary(s);
}

pour ceux d'entre vous qui sont intéressés par le code actuel derrière le un-liners de FractalizeR (j'en avais besoin depuis javax.XML.lier n'est pas disponible pour Android (par défaut), cela vient de com.soleil.XML.interne.lier.DatatypeConverterImpl.java :

public byte[] parseHexBinary(String s) {
    final int len = s.length();

    // "111" is not a valid hex encoding.
    if( len%2 != 0 )
        throw new IllegalArgumentException("hexBinary needs to be even-length: "+s);

    byte[] out = new byte[len/2];

    for( int i=0; i<len; i+=2 ) {
        int h = hexToBin(s.charAt(i  ));
        int l = hexToBin(s.charAt(i+1));
        if( h==-1 || l==-1 )
            throw new IllegalArgumentException("contains illegal character for hexBinary: "+s);

        out[i/2] = (byte)(h*16+l);
    }

    return out;
}

private static int hexToBin( char ch ) {
    if( '0'<=ch && ch<='9' )    return ch-'0';
    if( 'A'<=ch && ch<='F' )    return ch-'A'+10;
    if( 'a'<=ch && ch<='f' )    return ch-'a'+10;
    return -1;
}

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length*2);
    for ( byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}
20
répondu Bert Regelink 2017-05-23 12:03:09

Voici une méthode qui fonctionne réellement (basée sur plusieurs réponses semi-correctes précédentes):

private static byte[] fromHexString(final String encoded) {
    if ((encoded.length() % 2) != 0)
        throw new IllegalArgumentException("Input string must contain an even number of characters");

    final byte result[] = new byte[encoded.length()/2];
    final char enc[] = encoded.toCharArray();
    for (int i = 0; i < enc.length; i += 2) {
        StringBuilder curr = new StringBuilder(2);
        curr.append(enc[i]).append(enc[i + 1]);
        result[i/2] = (byte) Integer.parseInt(curr.toString(), 16);
    }
    return result;
}

le seul problème possible que je puisse voir est si la chaîne de saisie est extrêmement longue; appeler tocharray() fait une copie du tableau interne de la chaîne.

EDIT: Oh, et par la manière, les octets sont signés en Java, de sorte que votre chaîne d'entrée convertit à [0, -96, -65] au lieu de [0, 160, 191]. Mais tu le savais probablement déjà.

14
répondu Michael Myers 2008-09-26 16:14:56

Dans android ,si vous travaillez avec hex, vous pouvez essayer okio .

usage simple:

byte[] bytes = ByteString.decodeHex("c000060000").toByteArray();

et le résultat sera

[-64, 0, 6, 0, 0]
8
répondu Miao1007 2015-11-04 13:25:02

EDIT: comme souligné par @mmyers, cette méthode ne fonctionne pas sur les entrées qui contiennent des sous - chaînes correspondant à des octets avec le jeu de bits élevé ("80" - "FF"). L'explication se trouve à numéro du bogue: 6259307 Byte.parseoctet ne fonctionne pas comme annoncé dans la Documentation SDK .

public static final byte[] fromHexString(final String s) {
    byte[] arr = new byte[s.length()/2];
    for ( int start = 0; start < s.length(); start += 2 )
    {
        String thisByte = s.substring(start, start+2);
        arr[start/2] = Byte.parseByte(thisByte, 16);
    }
    return arr;
}
3
répondu Blair Conrad 2008-09-26 16:41:16

le Code présenté par Bert Regelink ne fonctionne tout simplement pas. Essayez ce qui suit:

import javax.xml.bind.DatatypeConverter;
import java.io.*;

public class Test
{  
    @Test
    public void testObjectStreams( ) throws IOException, ClassNotFoundException
    {     
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);

            String stringTest = "TEST";
            oos.writeObject( stringTest );

            oos.close();
            baos.close();

            byte[] bytes = baos.toByteArray();
            String hexString = DatatypeConverter.printHexBinary( bytes);
            byte[] reconvertedBytes = DatatypeConverter.parseHexBinary(hexString);

            assertArrayEquals( bytes, reconvertedBytes );

            ByteArrayInputStream bais = new ByteArrayInputStream(reconvertedBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);

            String readString = (String) ois.readObject();

            assertEquals( stringTest, readString);
        }
    }
3
répondu Sean Coffey 2012-11-08 19:51:39

pour ce que ça vaut, voici une autre version qui supporte les chaînes de longueurs impaires, sans recourir à la concaténation des chaînes.

public static byte[] hexStringToByteArray(String input) {
    int len = input.length();

    if (len == 0) {
        return new byte[] {};
    }

    byte[] data;
    int startIdx;
    if (len % 2 != 0) {
        data = new byte[(len / 2) + 1];
        data[0] = (byte) Character.digit(input.charAt(0), 16);
        startIdx = 1;
    } else {
        data = new byte[len / 2];
        startIdx = 0;
    }

    for (int i = startIdx; i < len; i += 2) {
        data[(i + 1) / 2] = (byte) ((Character.digit(input.charAt(i), 16) << 4)
                + Character.digit(input.charAt(i+1), 16));
    }
    return data;
}
3
répondu Conor Svensson 2016-10-11 04:16:05

j'ai toujours utilisé une méthode comme

public static final byte[] fromHexString(final String s) {
    String[] v = s.split(" ");
    byte[] arr = new byte[v.length];
    int i = 0;
    for(String val: v) {
        arr[i++] =  Integer.decode("0x" + val).byteValue();

    }
    return arr;
}

cette méthode se divise sur des valeurs HEX délimitées par des espaces, mais il ne serait pas difficile de la faire diviser la chaîne sur d'autres critères tels que des regroupements de deux caractères.

2
répondu pfranza 2008-09-26 15:18:41

la méthode BigInteger() de java.les maths sont très lentes et pas recommandables.

Integer.parseInt(HEXString, 16)

peut causer des problèmes avec certains caractères sans conversion en chiffre / entier

"151940920 une méthode de Travail:

Integer.decode("0xXX") .byteValue()

fonction:

public static byte[] HexStringToByteArray(String s) {
    byte data[] = new byte[s.length()/2];
    for(int i=0;i < s.length();i+=2) {
        data[i/2] = (Integer.decode("0x"+s.charAt(i)+s.charAt(i+1))).byteValue();
    }
    return data;
}

Amusez-Vous, Bonne Chance

2
répondu Sniper 2009-11-09 19:17:24

j'aime le personnage.chiffres solution, mais voici comment je l'ai résolu

public byte[] hex2ByteArray( String hexString ) {
    String hexVal = "0123456789ABCDEF";
    byte[] out = new byte[hexString.length() / 2];

    int n = hexString.length();

    for( int i = 0; i < n; i += 2 ) {
        //make a bit representation in an int of the hex value 
        int hn = hexVal.indexOf( hexString.charAt( i ) );
        int ln = hexVal.indexOf( hexString.charAt( i + 1 ) );

        //now just shift the high order nibble and add them together
        out[i/2] = (byte)( ( hn << 4 ) | ln );
    }

    return out;
}
2
répondu Kernel Panic 2010-08-04 17:39:40

J'ai trouvé que la panique du noyau avait la solution la plus utile pour moi, mais j'ai rencontré des problèmes si la chaîne hex était un nombre impair. résolu de cette façon:

boolean isOdd(int value)
{
    return (value & 0x01) !=0;
}

private int hexToByte(byte[] out, int value)
{
    String hexVal = "0123456789ABCDEF"; 
    String hexValL = "0123456789abcdef";
    String st = Integer.toHexString(value);
    int len = st.length();
    if (isOdd(len))
        {
        len+=1; // need length to be an even number.
        st = ("0" + st);  // make it an even number of chars
        }
    out[0]=(byte)(len/2);
    for (int i =0;i<len;i+=2)
    {
        int hh = hexVal.indexOf(st.charAt(i));
            if (hh == -1)  hh = hexValL.indexOf(st.charAt(i));
        int lh = hexVal.indexOf(st.charAt(i+1));
            if (lh == -1)  lh = hexValL.indexOf(st.charAt(i+1));
        out[(i/2)+1] = (byte)((hh << 4)|lh);
    }
    return (len/2)+1;
}

j'ajoute un certain nombre de nombres hexadécimaux à un tableau, donc je passe la référence au tableau que j'utilise, et l'int j'ai besoin converti et retournant la position relative du prochain nombre hexadécimal. Ainsi, le dernier tableau d'octets a [0] Nombre de paires hexadécimales, [1...] paires hexadécimales, puis le nombre de paires...

1
répondu Clayton Balabanov 2012-03-24 18:27:45

sur la base de la solution op votée, la suivante devrait être un peu plus efficace:

  public static byte [] hexStringToByteArray (final String s) {
    if (s == null || (s.length () % 2) == 1)
      throw new IllegalArgumentException ();
    final char [] chars = s.toCharArray ();
    final int len = chars.length;
    final byte [] data = new byte [len / 2];
    for (int i = 0; i < len; i += 2) {
      data[i / 2] = (byte) ((Character.digit (chars[i], 16) << 4) + Character.digit (chars[i + 1], 16));
    }
    return data;
  }

parce que: la conversion initiale en un tableau de caractères épargne les contrôles de longueur dans charAt

1
répondu Philip Helger 2013-05-16 14:43:47

si vous avez une préférence pour Java 8 streams comme votre style de codage, alors cela peut être réalisé en utilisant seulement des primitives JDK.

String hex = "0001027f80fdfeff";

byte[] converted = IntStream.range(0, hex.length() / 2)
    .map(i -> Character.digit(hex.charAt(i * 2), 16) << 4 | Character.digit(hex.charAt((i * 2) + 1), 16))
    .collect(ByteArrayOutputStream::new,
             ByteArrayOutputStream::write,
             (s1, s2) -> s1.write(s2.toByteArray(), 0, s2.size()))
    .toByteArray();

les paramètres , 0, s2.size() de la fonction collector concatenate peuvent être omis si cela ne vous dérange pas d'attraper IOException .

1
répondu Andy Brown 2018-04-16 09:18:54
public static byte[] hex2ba(String sHex) throws Hex2baException {
    if (1==sHex.length()%2) {
        throw(new Hex2baException("Hex string need even number of chars"));
    }

    byte[] ba = new byte[sHex.length()/2];
    for (int i=0;i<sHex.length()/2;i++) {
        ba[i] = (Integer.decode(
                "0x"+sHex.substring(i*2, (i+1)*2))).byteValue();
    }
    return ba;
}
0
répondu David V 2010-03-15 15:46:07

ma solution formelle:

/**
 * Decodes a hexadecimally encoded binary string.
 * <p>
 * Note that this function does <em>NOT</em> convert a hexadecimal number to a
 * binary number.
 *
 * @param hex Hexadecimal representation of data.
 * @return The byte[] representation of the given data.
 * @throws NumberFormatException If the hexadecimal input string is of odd
 * length or invalid hexadecimal string.
 */
public static byte[] hex2bin(String hex) throws NumberFormatException {
    if (hex.length() % 2 > 0) {
        throw new NumberFormatException("Hexadecimal input string must have an even length.");
    }
    byte[] r = new byte[hex.length() / 2];
    for (int i = hex.length(); i > 0;) {
        r[i / 2 - 1] = (byte) (digit(hex.charAt(--i)) | (digit(hex.charAt(--i)) << 4));
    }
    return r;
}

private static int digit(char ch) {
    int r = Character.digit(ch, 16);
    if (r < 0) {
        throw new NumberFormatException("Invalid hexadecimal string: " + ch);
    }
    return r;
}

est comme la fonction PHP hex2bin () mais dans le style Java.

exemple:

String data = new String(hex2bin("6578616d706c65206865782064617461"));
// data value: "example hex data"
0
répondu Daniel De León 2017-05-05 08:24:39

tard au parti, mais j'ai amalgamé la réponse ci - dessus par DaveL dans une classe avec l'action inverse-juste au cas où il aide.

public final class HexString {
    private static final char[] digits = "0123456789ABCDEF".toCharArray();

    private HexString() {}

    public static final String fromBytes(final byte[] bytes) {
        final StringBuilder buf = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            buf.append(HexString.digits[(bytes[i] >> 4) & 0x0f]);
            buf.append(HexString.digits[bytes[i] & 0x0f]);
        }
        return buf.toString();
    }

    public static final byte[] toByteArray(final String hexString) {
        if ((hexString.length() % 2) != 0) {
            throw new IllegalArgumentException("Input string must contain an even number of characters");
        }
        final int len = hexString.length();
        final byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                    + Character.digit(hexString.charAt(i + 1), 16));
        }
        return data;
    }
}

et classe D'essai JUnit:

public class TestHexString {

    @Test
    public void test() {
        String[] tests = {"0FA1056D73", "", "00", "0123456789ABCDEF", "FFFFFFFF"};

        for (int i = 0; i < tests.length; i++) {
            String in = tests[i];
            byte[] bytes = HexString.toByteArray(in);
            String out = HexString.fromBytes(bytes);
            System.out.println(in); //DEBUG
            System.out.println(out); //DEBUG
            Assert.assertEquals(in, out);

        }

    }

}
0
répondu DrPhill 2018-08-25 09:03:04

je sais que c'est un fil très ancien, mais j'aime quand même ajouter ma valeur.

si j'ai vraiment besoin de coder une simple chaîne hex en convertisseur binaire, j'aimerais le faire comme suit.

public static byte[] hexToBinary(String s){

  /*
   * skipped any input validation code
   */

  byte[] data = new byte[s.length()/2];

  for( int i=0, j=0; 
       i<s.length() && j<data.length; 
       i+=2, j++)
  {
     data[j] = (byte)Integer.parseInt(s.substring(i, i+2), 16);
  }

  return data;
}
0
répondu tigger 2018-09-25 15:27:59

de loin pas la solution la plus propre. Mais cela fonctionne pour moi et est bien formaté:

private String createHexDump(byte[] msg, String description) {
    System.out.println();
    String result = "\n" + description;
    int currentIndex = 0;
    for(int i=0 ; i<msg.length ; i++){
        currentIndex++;
        if(i == 0){
            result += String.format("\n  %04x ", i);
        }
        if(i % 16 == 0 && i != 0){
            result += " | ";
            for(int j=(i-16) ; j<msg.length && j<i ; j++) {
                char characterToAdd = (char) msg[j];
                if (characterToAdd == '\n') {
                    characterToAdd = ' ';
                }
                result += characterToAdd;
            }

            result += String.format("\n  %04x ", i);
        }

        result += String.format("%02x ", msg[i]);
    }

    if(currentIndex % 16 != 0){
        int fitIns = msg.length / 16;
        int leftOvers = msg.length - (fitIns * 16);
        for(int i=0 ; i<16-leftOvers ; i++){
            result += "   ";
        }

        result += " | ";

        for(int i=msg.length-leftOvers ; i<msg.length ; i++){
            char characterToAdd = (char) msg[i];
            if (characterToAdd == '\n') {
                characterToAdd = ' ';
            }
            result += characterToAdd;
        }
    }

    result += "\n";

    return result;
}

La sortie:

  S -> C
    0000 0b 00 2e 06 4d 6f 72 69 74 7a 53 6f 6d 65 20 54  |  .Heyyy Some T
    0010 43 50 20 73 74 75 66 66 20 49 20 63 61 70 74 75  | CP stuff I captu
    0020 72 65 64 2e 2e 77 65 6c 6c 20 66 6f 72 6d 61 74  | red..well format
    0030 3f                                               | ?
-1
répondu Moritz Schmidt 2018-05-04 11:52:02

je pense que je vais le faire pour vous. Je l'ai bricolé ensemble à partir d'une fonction similaire qui a retourné les données comme une chaîne de caractères:

private static byte[] decode(String encoded) {
    byte result[] = new byte[encoded/2];
    char enc[] = encoded.toUpperCase().toCharArray();
    StringBuffer curr;
    for (int i = 0; i < enc.length; i += 2) {
        curr = new StringBuffer("");
        curr.append(String.valueOf(enc[i]));
        curr.append(String.valueOf(enc[i + 1]));
        result[i] = (byte) Integer.parseInt(curr.toString(), 16);
    }
    return result;
}
-2
répondu Bob King 2008-09-26 15:21:54

Pour Moi, c'était la solution, HEX="FF01" puis coupé à la FF(255) et 01(01)

private static byte[] BytesEncode(String encoded) {
    //System.out.println(encoded.length());
    byte result[] = new byte[encoded.length() / 2];
    char enc[] = encoded.toUpperCase().toCharArray();
    String curr = "";
    for (int i = 0; i < encoded.length(); i=i+2) {
        curr = encoded.substring(i,i+2);
        System.out.println(curr);
        if(i==0){
            result[i]=((byte) Integer.parseInt(curr, 16));
        }else{
            result[i/2]=((byte) Integer.parseInt(curr, 16));
        }

    }
    return result;
}
-2
répondu Alejandro 2014-12-06 00:58:49