442

I am looking for a way to convert a long string (from a dump), that represents hex values into a byte array.

I couldn't have phrased it better than the person that posted the same question here.

But to keep it original, I'll phrase it my own way: suppose I have a string "00A0BF" that I would like interpreted as the

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

what should I do?

I am a Java novice and ended up using BigInteger and watching out for leading hex zeros. But I think it is ugly and I am sure I am missing something simple.

rafraf
  • 4,806
  • 3
  • 19
  • 20
  • See also https://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java. – flow2k Oct 23 '18 at 18:54
  • I have tamed `BigInteger` [here](https://stackoverflow.com/a/53463843). – John McClane Nov 25 '18 at 01:34
  • FWIW `String.getBytes()` won't work like you think it might. Had to learn this the hard way. `if ("FF".getBytes() != "ff".getBytes()) { System.out.println("Try again"); }` – tir38 Sep 11 '19 at 17:05

25 Answers25

759

Update (2021) - Java 17 now includes java.util.HexFormat (only took 25 years):

HexFormat.of().parseHex(s)


For older versions of Java:

Here's a solution that I think is better than any posted so far:

/* s must be an even-length string. */
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;
}

Reasons why it is an improvement:

  • Safe with leading zeros (unlike BigInteger) and with negative byte values (unlike Byte.parseByte)

  • Doesn't convert the String into a char[], or create StringBuilder and String objects for every single byte.

  • No library dependencies that may not be available

Feel free to add argument checking via assert or exceptions if the argument is not known to be safe.

Dave L.
  • 43,907
  • 11
  • 63
  • 62
  • Thanks. There should be a built-in for this. Especially that Byte.parseByte croaks on negative values is cumbersome. – Thilo Mar 14 '10 at 11:02
  • Produces a wrong result. See the apache implementation in the below post. – Gabriel Llamas Apr 16 '11 at 12:04
  • 3
    Can you give an example that is decoded incorrectly, or explain how it's wrong? – Dave L. Apr 17 '11 at 14:35
  • 6
    It doesn't work for the String "0". It throws an java.lang.StringIndexOutOfBoundsException – ovdsrn Jun 08 '11 at 20:06
  • 56
    "0" is not valid input. Bytes require two hexidecimal digits each. As the answer notes, "Feel free to add argument checking...if the argument is not known to be safe." – Dave L. Jun 09 '11 at 16:42
  • 16
    javax.xml.bind.DatatypeConverter.parseHexBinary(hexString) seems to be about 20% faster than the above solution in my micro tests (for whatever little they are worth), as well as correctly throwing exceptions on invalid input (e.g. "gg" is not a valid hexString but will return -77 using the solution as proposed). – Trevor Freeman Apr 04 '12 at 18:31
  • I don't understand this: Safe with leading zeros (unlike BigInteger) and with negative byte values (unlike Byte.parseByte), how are these two unsafe? Could you give me any examples for testing, please? – Mohamed El-Nakeep Mar 19 '14 at 14:29
  • 1
    @MuhammadAnnaqeeb See other answers below that use BigInteger or Byte.parseByte – Dave L. Mar 19 '14 at 18:03
  • @Dave Shouldn't this be using logical shift instead of arithmetic shift? – Chef Pharaoh Oct 22 '14 at 19:44
  • @ChefPharaoh There is only a single left shift operator in Java which has the same effect as logical or arithmetic shift. See http://en.wikipedia.org/wiki/Logical_shift – Dave L. Oct 22 '14 at 23:06
  • How does a hex dump looks like. Can some one give an example – Arun George Jan 22 '15 at 22:57
  • I'm wondering if `data[i / 2] = (byte) (((Character.digit(s.charAt(i), 16) << 4) | Character.digit(s.charAt(i + 1), 16)));` could be slightly more optimal? Or maybe a `0xFF` mask could also be necessary. – Brett Ryan Feb 20 '15 at 07:54
  • If the for-statement is changed to `for (int i = 0; i < len - 1; i += 2)` the function will no longer throw exceptions for data of invalid length. It still won't return correct data in those cases, but at least there is no longer a crash. – DaedalusAlpha May 13 '15 at 11:22
  • 8
    @DaedalusAlpha It depends on your context, but usually I find it's better to fail fast and loud with such things so that you can fix your assumptions rather than silently returning incorrect data. – Dave L. Jun 17 '15 at 16:03
  • 1
    Works like a charm. For the index of out of bounds issue prefix hex string with "0"'s to make the string to what ever size you need the output to be - for example if you are converting a 2 byte hex string do this before calling the method StringUtils.leftPad(hextString, 4, "0"). Two characters in hex string is converted to 1 byte. – Rohan Dec 11 '15 at 01:25
  • I added a reason... when developing for android/linux/windows/macosx/ios in one source base, this solution always works. – Erik Aronesty Jul 24 '18 at 20:02
  • I would strongly disagree with '"0" is not valid input.' `0` is a perfectly valid hex string. Just because it isn't safe for this code doesn't mean it's not valid. The fact that each byte is normally expressed as two hex characters doesn't mean `0` isn't a valid hex string; hex strings can be converted to many things besides byte arrays. If the function requires an even-length hex string as input, that should be clearly documented, instead of leaving it to the user to analyze the code in order to know what its assumptions are. https://garethrees.org/2014/05/08/heartbleed/ – LarsH Aug 04 '20 at 18:55
  • 1
    @LarsH Thanks for the reminder that it's always good to be clear about one's assumptions. In the context of this question, about a hex dump, with an example using pairs of characters, including leading zeros, I think it's a fair assumption that the data should represent bytes using two characters each. However, I can imagine domains where one would want to allow the special case of a single or leading '0' character instead. I agree that if this function is part of a public API that behavior either way should be documented, as it is in org.apache.commons.codec.binary.Hex and the XML datatypes – Dave L. Aug 04 '20 at 22:21
  • Fair enough. The question title does say "hex dump," not just "hex string." BTW I just used your code in a production project. (And added a check for odd-length strings.) So thanks. – LarsH Aug 05 '20 at 15:46
  • P.S. I made a proposed edit to document the assumption. Obviously you can change it as desired. – LarsH Aug 05 '20 at 16:04
  • 104207194088 TESTINGG 4304 GG2741 ��ôCONN���|��$GK23000023R00P08TESTINGG104207194088201026072003BVodafone IN��������������;�� - > Getting this "??" type of response while receiving data using datagram socket. Any suggestion where i am going wrong? – Bhavin Chauhan Oct 26 '20 at 07:21
  • My Hex Dumps contain blanks. "/2" won't work for me. – Sam Ginrich Jul 02 '23 at 11:26
354

One-liners:

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

Warnings:

  • in Java 9 Jigsaw this is no longer part of the (default) java.se root set so it will result in a ClassNotFoundException unless you specify --add-modules java.se.ee (thanks to @eckes)
  • Not available on Android (thanks to Fabian for noting that), but you can just take the source code if your system lacks javax.xml for some reason. Thanks to @Bert Regelink for extracting the source.
Community
  • 1
  • 1
Vladislav Rastrusny
  • 29,378
  • 23
  • 95
  • 156
  • 18
    IMHO this should be the accepted/top answer since it's short and *cleanish* (unlike @DaveL's answer) and doesn't require any external libs (like skaffman's answer). Also, **. – Priidu Neemre Aug 18 '15 at 12:40
  • 11
    the datatypeconverter class is not available in android for example. – Fabian Feb 10 '16 at 12:41
  • 4
    Warning: in Java 9 Jigsaw this is no longer part of the (default) `java.se` root set so it will result in a `ClassNotFoundException` unless you specify `--add-modules java.se.ee` – eckes Nov 10 '16 at 12:17
  • Amazing answer, could decode a base64 string request, used hex to convert the byte array with the provided solution and it worked like a charm – dantebarba Jan 06 '17 at 19:20
  • 3
    @dantebarba I think `javax.xml.bind.DatatypeConverter` already provides a method for encoding/decoding Base64 data. See `parseBase64Binary()` and `printBase64Binary()`. – DragShot Jul 03 '17 at 22:18
  • can confirm that depending on javax.xml.bind can cause java.lang.NoClassDefFoundError. – Dmytro May 14 '18 at 22:40
  • 2
    To add to the issues with `DataTypeConverter`, Java SE 11 has removed the JAXB API entirely and is now only included with Java EE. You can also add it as a Maven dependency, as suggested here: https://stackoverflow.com/a/43574427/7347751 – David Mordigal Jan 30 '19 at 05:51
111

The Hex class in commons-codec should do that for you.

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

import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF
Utgarda
  • 686
  • 4
  • 23
skaffman
  • 398,947
  • 96
  • 818
  • 769
  • 7
    This also looks good. See org.apache.commons.codec.binary.Hex.decodeHex() – Dave L. Sep 26 '08 at 17:46
  • It was interesting. But I found their solution hard to follow. Does it have any advantages over what you proposed (other than checking for even number of chars)? – rafraf Sep 27 '08 at 01:06
46

You can now use BaseEncoding in guava to accomplish this.

BaseEncoding.base16().decode(string);

To reverse it use

BaseEncoding.base16().encode(bytes);
jontro
  • 10,241
  • 6
  • 46
  • 71
37

Actually, I think the BigInteger is solution is very nice:

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

Edit: Not safe for leading zeros, as noted by the poster.

Dave L.
  • 43,907
  • 11
  • 63
  • 62
  • 1
    I also thought so initially. And thank you for documenting it - I was just thinking I should... it did some strange things though that I didn't really understand - like omit some leading 0x00 and also mix up the order of 1 byte in a 156 byte string I was playing with. – rafraf Sep 26 '08 at 16:43
  • 2
    That's a good point about leading 0's. I'm not sure I believe it could mix up the order of bytes, and would be very interested to see it demonstrated. – Dave L. Sep 26 '08 at 16:55
  • 1
    yeah, as soon as I said it, I didn't believe me either :) I ran a compare of the byte array from BigInteger with mmyers'fromHexString and (with no 0x00) against the offending string - they were identical. The "mix up" did happen, but it may have been something else. I willlook more closely tomorrow – rafraf Sep 26 '08 at 17:09
  • 5
    The issue with BigInteger is that there must be a "sign bit". If the leading byte has the high bit set then the resulting byte array has an extra 0 in the 1st position. But still +1. – Gray Oct 28 '11 at 16:20
  • This adds a leading 0 for negative values. I assume that `BigInteger` expects the value to be unsigned and thus fails where bit positions are essential – PowerAktar Oct 03 '22 at 12:25
27

One-liners:

import jakarta.xml.bind.DatatypeConverter;

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

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

For those of you interested in the actual code behind the One-liners from FractalizeR (I needed that since javax.xml.bind is not available for Android and Java 9+ by default), this comes from jakarta.xml.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 < 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();
}
Sandwich
  • 35
  • 2
  • 11
Bert Regelink
  • 2,696
  • 23
  • 17
  • 4
    DatatypeConverter is also not available in Java 9 by default. The dangerous thing is code using it will compile under Java 1.8 or earlier (Java 9 with source settings to earlier), but get a runtime exception under Java 9 without "--add-modules java.se.ee". – Stephen M -on strike- Aug 28 '17 at 16:54
24

The HexBinaryAdapter provides the ability to marshal and unmarshal between String and byte[].

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

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

That's just an example I typed in...I actually just use it as is and don't need to make a separate method for using it.

Anil Bharadia
  • 2,760
  • 6
  • 34
  • 46
GrkEngineer
  • 2,122
  • 19
  • 20
  • 6
    It works only if the input string (hexString) has an even number of characters. Otherwise: Exception in thread "main" java.lang.IllegalArgumentException: hexBinary needs to be even-length: – ovdsrn Jun 08 '11 at 20:15
  • 4
    Oh, thanks for pointing that out. A user really shouldn't have an odd number of characters because the byte array is represented as {0x00,0xA0,0xBf}. Each byte has two hex digits or nibbles. So any number of bytes should always have an even number of characters. Thanks for mentioning this. – GrkEngineer Jun 16 '11 at 15:54
  • 8
    You can use java.xml.bind.DatatypeConverter.parseHexBinary(hexString) directly instead of using HexBinaryAdapter (which in turn calls DatatypeConverter). This way you do not have to create an adapter instance object (since DatatypeConverter methods are static). – Trevor Freeman Apr 04 '12 at 18:33
  • javax.xml.bind.* is no longer available in Java 9. The dangerous thing is code using it will compile under Java 1.8 or earlier (Java 9 with source settings to earlier), but get a runtime exception running under Java 9. – Stephen M -on strike- Aug 28 '17 at 16:52
17

Here is a method that actually works (based on several previous semi-correct answers):

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

The only possible issue that I can see is if the input string is extremely long; calling toCharArray() makes a copy of the string's internal array.

EDIT: Oh, and by the way, bytes are signed in Java, so your input string converts to [0, -96, -65] instead of [0, 160, 191]. But you probably knew that already.

Michael Myers
  • 188,989
  • 46
  • 291
  • 292
  • 1
    Thanks Michael - you're a life saver! Working on a BlackBerry project and trying to convert a string representation of a byte back into the byte ... using RIM's "Byte.parseByte( byteString, 16 )" method. Kept throwing a NumberFormatExcpetion. Spent hours tyring to figure out why. Your suggestion of "Integer.praseInt()" did the trick. Thanks again!! – BonanzaDriver Oct 23 '11 at 19:28
15

In android ,if you are working with hex, you can try okio.

simple usage:

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

and result will be

[-64, 0, 6, 0, 0]
Miao1007
  • 944
  • 6
  • 22
6

EDIT: as pointed out by @mmyers, this method doesn't work on input that contains substrings corresponding to bytes with the high bit set ("80" - "FF"). The explanation is at Bug ID: 6259307 Byte.parseByte not working as advertised in the SDK Documentation.

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;
}
Martin
  • 2,573
  • 28
  • 22
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
6

The BigInteger() Method from java.math is very Slow and not recommandable.

Integer.parseInt(HEXString, 16)

can cause problems with some characters without converting to Digit / Integer

a Well Working method:

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

Function:

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

Have Fun, Good Luck

Sniper
  • 61
  • 1
  • 2
4

For what it's worth, here's another version which supports odd length strings, without resorting to string concatenation.

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;
}
Conor Svensson
  • 1,151
  • 11
  • 17
3

I like the Character.digit solution, but here is how I solved it

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

The Code presented by Bert Regelink simply does not work. Try the following:

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);
        }
    }
Gábor Lipták
  • 9,646
  • 2
  • 59
  • 113
Sean Coffey
  • 29
  • 1
  • 3
2

I've always used a method like

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

this method splits on space delimited hex values but it wouldn't be hard to make it split the string on any other criteria such as into groupings of two characters.

pfranza
  • 3,292
  • 2
  • 21
  • 34
  • The string concatenation is unnecessary. Just use Integer.valueOf(val, 16). – Michael Myers Sep 26 '08 at 15:21
  • I've tried using the radix conversions like that before and I've had mixed results – pfranza Sep 26 '08 at 15:23
  • thanks - oddly it works fine with this string: "9C001C" or "001C21" and fails with this one: "9C001C21" Exception in thread "main" java.lang.NumberFormatException: For input string: "9C001C21" at java.lang.NumberFormatException.forInputString(Unknown Source) – rafraf Sep 26 '08 at 16:07
  • (That's not more odd than in the `Byte`/`byte` case: highest bit set without leading -) – greybeard Feb 13 '16 at 18:17
1

Based on the op voted solution, the following should be a bit more efficient:

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

Because: the initial conversion to a char array spares the length checks in charAt

Philip Helger
  • 1,814
  • 18
  • 28
1

If you have a preference for Java 8 streams as your coding style then this can be achieved using just JDK primitives.

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

The , 0, s2.size() parameters in the collector concatenate function can be omitted if you don't mind catching IOException.

Andy Brown
  • 11,766
  • 2
  • 42
  • 61
1

If your needs are more than just the occasional conversion then you can use HexUtils.

Example:

byte[] byteArray = Hex.hexStrToBytes("00A0BF");

This is the most simple case. Your input may contain delimiters (think MAC addresses, certificate thumbprints, etc), your input may be streaming, etc. In such cases it gets easier to justify to pull in an external library like HexUtils, however small.

With JDK 17 the HexFormat class will fulfill most needs and the need for something like HexUtils is greatly diminished. However, HexUtils can still be used for things like converting very large amounts to/from hex (streaming) or pretty printing hex (think wire dumps) which the JDK HexFormat class cannot do.

(full disclosure: I'm the author of HexUtils)

lbruun
  • 241
  • 1
  • 6
1

I found Kernel Panic to have the solution most useful to me, but ran into problems if the hex string was an odd number. solved it this way:

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

I am adding a number of hex numbers to an array, so i pass the reference to the array I am using, and the int I need converted and returning the relative position of the next hex number. So the final byte array has [0] number of hex pairs, [1...] hex pairs, then the number of pairs...

0
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

My formal solution:

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

Is like the PHP hex2bin() Function but in Java style.

Example:

String data = new String(hex2bin("6578616d706c65206865782064617461"));
// data value: "example hex data"
Daniel De León
  • 13,196
  • 5
  • 87
  • 72
0

Late to the party, but I have amalgamated the answer above by DaveL into a class with the reverse action - just in case it helps.

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

And JUnit test class:

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

        }

    }

}
DrPhill
  • 614
  • 5
  • 16
0

I know this is a very old thread, but still like to add my penny worth.

If I really need to code up a simple hex string to binary converter, I'd like to do it as follows.

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;
}
tigger
  • 37
  • 3
-2

I think will do it for you. I cobbled it together from a similar function that returned the data as a string:

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;
}
Bob King
  • 25,372
  • 6
  • 54
  • 66
  • First, you shouldn't need to convert the string to uppercase. Second, it is possible to append chars directly to a StringBuffer, which should be much more efficient. – Michael Myers Sep 26 '08 at 15:48
  • For that matter you don't need any StringBuffer (which since 2004 could better be StringBuilder), just do `new String (enc, i, 2)` – dave_thompson_085 Jan 03 '22 at 17:53
-2

For Me this was the solution, HEX="FF01" then split to FF(255) and 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;
}
Alejandro
  • 1
  • 2
  • This question has been answered for a while and has several good alternatives in place; unfortunately, your answer does not provide any significantly improved value at this point. – rfornal Dec 06 '14 at 01:10