Convertire una rappresentazione di 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 i valori esadecimali in una matrice di byte.

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

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

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

cosa dovrei fare?

Sono un novizio di Java e ho finito per usare BigInteger e fare attenzione agli zeri esagonali iniziali. Ma penso che sia brutto e sono sicuro che mi manca qualcosa di semplice.

    Ecco una soluzione che ritengo sia migliore di qualsiasi postata 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[] , o crea oggetti StringBuilder e String per ogni singolo byte.

    • Nessuna dipendenza di libreria che potrebbe non essere disponibile

    Sentiti libero di aggiungere il controllo degli argomenti tramite assert o eccezioni se l'argomento non è noto per essere sicuro.

    One-liner:

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

    Avvertimenti :

    • in Java 9 Jigsaw questo non fa più parte del set di root java.se (predefinito) in modo che risulti in ClassNotFoundException a meno che non si specifichi –add-modules 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 non ha javax.xml per qualche motivo. Grazie a @ Bert Regelink per l’estrazione della fonte.

    La class Hex in common-codec dovrebbe farlo per te.

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

    Ora puoi usare BaseEncoding in guava per fare ciò.

     BaseEncoding.base16().decode(string); 

    Per invertire utilizzare

     BaseEncoding.base16().encode(bytes); 

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

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

    Modifica: non sicuro per gli zeri iniziali , come indicato dal poster.

    HexBinaryAdapter offre la possibilità di effettuare il marshalling 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 solo così com’è e non ho bisogno di fare un metodo separato per usarlo.

    One-liner:

     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 vero e proprio dietro a One-Liners di FractalizeR (ne avevo bisogno dal momento che javax.xml.bind non è disponibile per Android (di default)), questo deriva da com.sun.xml.internal.bind. 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> 4) & 0xF]); r.append(hexCode[(b & 0xF)]); } return r.toString(); } 

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

     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 ansible che riesco a vedere è se la stringa di input è estremamente lunga; chiamando aCharArray () crea una copia dell'array interno della stringa.

    EDIT: Oh, 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 già.

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

    uso semplice:

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

    e il risultato sarà

     [-64, 0, 6, 0, 0] 

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

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

    Per quello che vale, ecco un’altra versione che supporta stringhe di lunghezza dispari, senza ricorrere alla concatenazione di 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; } 

    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 da spazi, ma non sarebbe difficile fargli dividere la stringa su altri criteri come raggruppamenti di due caratteri.

    Il metodo BigInteger() di java.math è molto lento e non raccomandabile.

    Integer.parseInt(HEXString, 16)

    può causare problemi con alcuni caratteri senza conversione in Digit / Integer

    un metodo ben funzionante:

     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

    Mi piace la soluzione Character.digit, ma ecco come 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; } 

    Ho trovato Kernel Panic per avere la soluzione più utile per me, ma ho incontrato dei 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 

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

    Sulla base della soluzione op votata, il seguente 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 char risparmia i controlli di lunghezza in charAt

    Se hai una preferenza per i flussi Java 8 come stile di codifica, questo può essere ottenuto usando solo i primitivi 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 di concatenazione del collector possono essere omessi se non ti interessa catturare IOException .

     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 

    La mia soluzione formale:

     /** * Decodes a hexadecimally encoded binary string. * 

    * Note that this function does NOT 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" 

    In ritardo alla festa, ma ho amalgamato la risposta di cui sopra da DaveL in una class con l’azione inversa – nel caso in cui aiuta.

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

    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 

    Il risultato:

      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 | ? 

    Penso che lo farà per te. L’ho accartocciato da una funzione simile che ha restituito i dati sotto forma di 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; } 

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