Convertire una rappresentazione stringa di un dump esadecimale in un array di byte utilizzando Java?


Sto cercando un modo per convertire una stringa lunga (da un dump), che rappresenta valori esadecimali in un array di byte.

Non avrei potuto formularlo meglio della persona che ha pubblicato la stessa domanda qui.

Ma per mantenerlo originale, lo formulerò a modo mio: supponiamo di avere una stringa "00A0BF" che vorrei interpretata come

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

Cosa devo fare?

Sono un novizio Java e ho finito per usare BigInteger e guardare fuori per i primi zeri esadecimali. Ma penso è brutto e sono sicuro che mi manca qualcosa di semplice.

Author: rafraf, 2008-09-26

25 answers

Ecco una soluzione che penso sia migliore di qualsiasi pubblicata finora:

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

Motivi per cui è un miglioramento:

  • Sicuro con zeri iniziali (a differenza di BigInteger) e con valori di byte negativi (a differenza di Byte.parseByte)

  • Non converte la stringa in un char[], né crea StringBuilder e oggetti String per ogni singolo byte.

  • Nessuna dipendenza libreria che potrebbe non essere disponibile

Sentitevi liberi di aggiungere argomento controllo tramite assert o eccezioni se l'argomento non è noto per essere sicuro.

 544
Author: Dave L., 2018-07-24 20:02:07

Righe singole:

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

Avvertenze :

  • in Java 9 Jigsaw questo non fa più parte del (default) java.se radice impostare in modo che si tradurrà in una ClassNotFoundException a meno che non si specifichi -- aggiungere-moduli java.se.ee (grazie a @eckes)
  • Non disponibile su Android (grazie a Fabian per averlo notato), ma puoi semplicemente prendere il codice sorgente se il tuo sistema manca di javax.xml per qualche motivo. Grazie a @Bert Regelink per estrarre la fonte.
 307
Author: Vladislav Rastrusny, 2017-05-23 11:33:26

La classe Hex in commons-codec dovrebbe farlo per te.

Http://commons.apache.org/codec /

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

È ora possibile utilizzare BaseEncoding in guava per eseguire questa operazione.

BaseEncoding.base16().decode(string);

Per invertirlo usa

BaseEncoding.base16().encode(bytes);
 32
Author: jontro, 2018-02-01 15:51:27

In realtà, penso che la soluzione BigInteger sia molto bella:

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

Edit: Non sicuro per gli zeri iniziali , come indicato dal poster.

 22
Author: Dave L., 2008-09-26 17:47:08

Il HexBinaryAdapter fornisce la possibilità di marshal e unmarshal tra String e byte[].

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

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

Questo è solo un esempio che ho digitato...In realtà lo uso così com'è e non ho bisogno di creare un metodo separato per usarlo.

 21
Author: GrkEngineer, 2012-02-01 13:10:20

Righe singole:

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

Per quelli di voi interessati al codice reale dietro le One-liner da FractalizeR (ne avevo bisogno da javax.XML.bind non è disponibile per Android (per impostazione predefinita)), questo viene da com.sole.XML.interno.legare.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
Author: Bert Regelink, 2017-05-23 12:03:09

Ecco un metodo che funziona effettivamente (basato su diverse precedenti risposte semi-corrette):

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

L'unico problema possibile che posso vedere è se la stringa di input è estremamente lunga; chiamare toCharArray() crea una copia dell'array interno della stringa.

EDIT: Oh, e a proposito, i byte sono firmati in Java, quindi la stringa di input viene convertita in [0, -96, -65] invece di [0, 160, 191]. Ma probabilmente lo sapevi gia'.

 14
Author: Michael Myers, 2008-09-26 16:14:56

In Android ,se stai lavorando con hex, puoi provare okio .

Semplice utilizzo:

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

E il risultato sarà

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

EDIT: come sottolineato da @mmyers, questo metodo non funziona su input che contengono sottostringhe corrispondenti a byte con il bit alto impostato ("80" - "FF"). La spiegazione è a Bug ID: 6259307 Byte.parseByte non funziona come pubblicizzato nella documentazione 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
Author: Blair Conrad, 2008-09-26 16:41:16

Il Codice presentato da Bert Regelink semplicemente non funziona. Prova quanto segue:

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
Author: Sean Coffey, 2012-11-08 19:51:39

Per quello che vale, ecco un'altra versione che supporta stringhe di lunghezza dispari, senza ricorrere alla concatenazione delle stringhe.

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
Author: Conor Svensson, 2016-10-11 04:16:05

Ho sempre usato un metodo come

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

Questo metodo si divide su valori esadecimali delimitati dallo spazio, ma non sarebbe difficile dividere la stringa su altri criteri, ad esempio in raggruppamenti di due caratteri.

 2
Author: pfranza, 2008-09-26 15:18:41

Il metodo BigInteger() da java.la matematica è molto lenta e non raccomandabile.

Integer.parseInt(HEXString, 16)

Può causare problemi con alcuni caratteri senza conversione in cifre / interi

Un metodo di lavoro ben:

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

Funzione:

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

Buon divertimento, buona fortuna

 2
Author: Sniper, 2009-11-09 19:17:24

Mi piace il personaggio.soluzione digit, ma ecco come l'ho risolto

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
Author: Kernel Panic, 2010-08-04 17:39:40

Ho trovato Kernel Panic la soluzione più utile per me, ma ho incontrato problemi se la stringa esadecimale era un numero dispari. risolto in questo modo:

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

Sto aggiungendo un numero di numeri esadecimali a un array, quindi passo il riferimento all'array che sto usando, e l'int di cui ho bisogno convertito e restituendo la posizione relativa del prossimo numero esadecimale. Quindi l'array di byte finale ha [0] numero di coppie esadecimali, [1...] coppie esadecimali, quindi il numero di coppie...

 1
Author: Clayton Balabanov, 2012-03-24 18:27:45

In base alla soluzione op voted, quanto segue dovrebbe essere un po ' più efficiente:

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

Perché: la conversione iniziale in un array di caratteri risparmia i controlli di lunghezza in charAt

 1
Author: Philip Helger, 2013-05-16 14:43:47

Se hai una preferenza per i flussi Java 8 come stile di codifica, questo può essere ottenuto usando solo le primitive 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();

I parametri , 0, s2.size() nella funzione concatenazione collector possono essere omessi se non ti dispiace catturare IOException.

 1
Author: 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
Author: David V, 2010-03-15 15:46:07

La mia soluzione formale:

/**
 * 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;
}

È come la funzione PHP hex2bin () ma in stile Java.

Esempio:

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

In ritardo alla festa, ma ho amalgamato la risposta sopra da DaveL in una classe con l'azione inversa - nel caso in cui aiuti.

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

E classe di test 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
Author: DrPhill, 2018-08-25 09:03:04

So che questo è un thread molto vecchio, ma mi piace ancora aggiungere il mio penny worth.

Se ho davvero bisogno di codificare una semplice stringa esadecimale in un convertitore binario, mi piacerebbe farlo come segue.

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
Author: tigger, 2018-09-25 15:27:59

Di gran lunga non la soluzione più pulita. Ma funziona per me ed è ben formattato:

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

L'uscita:

  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
Author: Moritz Schmidt, 2018-05-04 11:52:02

Penso che lo farà per te. L'ho messo insieme da una funzione simile che ha restituito i dati come stringa:

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
Author: Bob King, 2008-09-26 15:21:54

Per me questa era la soluzione, HEX = "FF01" quindi diviso in FF(255) e 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
Author: Alejandro, 2014-12-06 00:58:49