Java: convert a byte array to a hex string?
Asked Answered
G

34

822

I have a byte array filled with hex numbers and printing it the easy way is pretty pointless because there are many unprintable elements. What I need is the exact hexcode in the form of: 3a5f771c

Gavrilla answered 11/3, 2012 at 13:6 Comment(5)
Why not just give it a try first and show us what you've got. You've nothing to lose and all to gain. Integer has a toHexString(...) method that may help if this is what you're looking for. Also String.format(...) can do some neat formatting tricks using the %2x code string.Gossip
possible duplicate of In Java, how do I convert a byte array to a string of hex digits while keeping leading zeros?Abcoulomb
With the help of stream in Java 8, it can be simply implemented as: static String byteArrayToHex(byte[] a) { return IntStream.range(0, a.length) .mapToObj(i -> String.format("%02x", a[i])) .reduce((acc, v) -> acc + " " + v) .get(); }Accusatorial
Java 17 to the rescue: HexFormat.of().formatHex(bytes)Ion
@Accusatorial You really call that "simple"?Caboose
M
1067

From the discussion here, and especially this answer, this is the function I currently use:

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

My own tiny benchmarks (a million bytes a thousand times, 256 bytes 10 million times) showed it to be much faster than any other alternative, about half the time on long arrays. Compared to the answer I took it from, switching to bitwise ops --- as suggested in the discussion --- cut about 20% off of the time for long arrays. (Edit: When I say it's faster than the alternatives, I mean the alternative code offered in the discussions. Performance is equivalent to Commons Codec, which uses very similar code.)

2k20 version, with respect to Java 9 compact strings:

private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static String bytesToHex(byte[] bytes) {
    byte[] hexChars = new byte[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars, StandardCharsets.UTF_8);
}
Mellott answered 24/3, 2012 at 20:32 Comment(20)
I just found javax.xml.bind.DataTypeConverter, part of the standard distribution. Why doesn't this come up when you Google this kind of problem? Lots helpful tools, including String printHexBinary(byte[]) and byte[] parseHexBinary(String). printHexBinary is, however, much (2x) slower than the function in this answer. (I checked the source; it uses a stringBuilder. parseHexBinary uses an array.) Really, though, for most purposes it's fast enough and you probably already have it.Mellott
+1 for the answer since Android does not have DataTypeConverterFern
@maybeWeCouldStealAVan: JDK 7 is now open source. We should submit a patch to improve performance for printHexBinary?Readytowear
@Mellott could you please explain how this works. I follow for the most part but really like understanding what is happening when using code. Thanks!Rego
This code is super-fast!! I was using String.format("%0" + (bytes.length << 1) + "X", new BigInteger(1, bytes)); but the performance was rubbish. This is a much better solution.Psychometrics
How to to reverse i.e. Given a Hex String get byte[] from the String ?Niveous
@Whome Hmm, how are you benchmarking it? In my benchmarks the speed looks exactly the same, which makes sense since each has the same number of array index lookups and the same number of operations. What advantage do you get from the larger table?Mellott
Great answer! A negligible performance increase could be obtained by adding a variable for n * 2 to be used in the first array index, then ++n in the second access.Certifiable
This solution is better than DataTypeConverter solution because it uses StringBuilder which inturn uses char[] and expand.If we know the size beforehand I feel char[] is a superior optionMyocardium
Is this code working for negative byte value? bcz I'm getting a value which is out of range for byte, when byte value is negative.Tourbillion
javax.xml.bind.DataTypeConverter is being removed from Java 11.Kenlay
Why is & 0xFF required in this line int v = bytes[j] & 0xFF;? Am I missing something or is it just unnecessary?Angelangela
@Angelangela Java only has signed number primitives, so a byte that you imagine as 0xFF will be thought of as negative. An array index is always int, and just using the byte variable as an index position will do the same as the method does by assigning it to int v: The negative byte will be expanded - the int will be full of set bits. You'd not be asking for array index 15 but something in the millions.Breault
Sometimes you need spaces, then use bytes.length * 3, hexChars[j * 3], hexChars[j * 3 + 1] and hexChars[j * 3 + 2] = 0x20Designedly
@Designedly did your spacing suggestion, got error: java.lang.NumberFormatException: For input string: " 16 BF 0C 13 C7 "Pelion
@Pelion well I just did bytesToHex(new byte[] {0x16,0x0C,0x13}) and it works fine. 0xBF and 0xC7 are not bytes. They are ints.Designedly
Should check empty bytes: if (bytes == null || bytes.length == 0) return ""; to make sure no crashQueri
Any benchmark does have more weight when done using jmh - github.com/openjdk/jmh If you have such benchmarks, please provide it here.Ashwin
@Mellott Thank you for your answer, it helped me a lot. I've slightly improved your solution in terms of performance by removing multiplications. The essential lines read like this: char[] hexChars = new char[bytes.length << 1]; for (int j=0, k=0; j<bytes.length; ) { int v=bytes[j++] & 0xFF; hexChars[k++] = HEX_ARRAY[v >>> 4]; hexChars[k++] = HEX_ARRAY[v & 0x0F]; } return new String(hexChars); According to my simple tests, the improvement is in the range of 5 to 25 percent.Ailis
@Ailis Interesting. When I wrote this (10 years ago, in Java 7 I believe) there was no speedup in using a shift in place of *2, so for legibility I wrote * when I was multiplying and >>> when I was extracting bits.Mellott
G
536

The Apache Commons Codec library has a Hex class for doing just this type of work.

import org.apache.commons.codec.binary.Hex;

String foo = "I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );
Goldiegoldilocks answered 11/3, 2012 at 13:18 Comment(12)
@cytinus - My downvote occurred 4 months ago so I'm not entirely certain what I was thinking, but I was probably objecting to the size of the library. This is a small function within the program; there's no need to add such a bulky library to the project to perform it.Metaxylem
@ArtOfWarefare I agree, so instead of import org.apache.commons.codec.*; you could do import org.apache.commons.codec.binary.Hex;Additive
@Metaxylem I have to disagree. The only terrible thing is that the apache commons libraries aren't included by default with the JRE and JDK. There are some libraries that are so useful they really should be on your class path by default, and this is one of them.Shaylynn
I highly recommend this answer is swapped as the top answer. Always vote to use a well tested, performant, open source library over custom code which doesn't improve on it.Transcendental
When using sbt or maven, adding an open-source dependency like this is dead-easy.Muscular
Also, commons-codec-1.9.jar is 258 KiB, and I guess the space impact can go further down if you are going to use Proguard or something like this.Muscular
Not to reinvigorate an old thread spuriously, but as added documentation for future reference: even working for some very large and particular companies, the Apache license was deemed acceptable for use in our commercial products. IMHO if you can't quickly drop in/manage 3rd party dependencies in your project for cases such as this, then you are not moving fast enough.Mulholland
+1 for suggesting an off-the-shelf library - Just to point out - this method returns a char[] array rather than a string, which tripped me up for a bit. (and the method appears now to 'encodeHex' rather than 'encodeHexString' - could be just the version I'm using though).Diametral
At the time I wrote that, there were methods for encodeHex and encodeHexString, though that might be different now.Goldiegoldilocks
Or in case you use BouncyCastle (org.bouncycastle:bcprov-jdk15on), you can use this class : org.bouncycastle.util.encoders.Hex, with this method : String toHexString(byte[] data)Sugden
Inside encodeHex, this below part of the code is fishy. Is it correct to use 0xF0 here?... ``` for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } ```Wilhelmina
and you get all the security flaws from bouncycastle. Do not use a library for such basic operationsCacodemon
A
351

The method javax.xml.bind.DatatypeConverter.printHexBinary(), part of the Java Architecture for XML Binding (JAXB), was a convenient way to convert a byte[] to a hex string. The DatatypeConverter class also included many other useful data-manipulation methods.

In Java 8 and earlier, JAXB was part of the Java standard library. It was deprecated with Java 9 and removed with Java 11, as part of an effort to move all Java EE packages into their own libraries. It's a long story. Now, javax.xml.bind doesn't exist, and if you want to use JAXB, which contains DatatypeConverter, you'll need to install the JAXB API and JAXB Runtime from Maven.

Example usage:

byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
String hex = javax.xml.bind.DatatypeConverter.printHexBinary(bytes);

Will result in:

000086003D

This answer the same as this one.

Abcoulomb answered 24/2, 2014 at 15:37 Comment(9)
A good solution, though sadly not one that is valid in Android.Mabel
@Mabel maybe you want to read code.google.com/p/dalvik/wiki/JavaxPackages. It is a way to get javax classes into Android. But if you only want convert to hex, it isn't worth the trouble.Abcoulomb
Any there any comments on the performance vs. the accepted answer. This is obviously much simpler.Tabber
Win7-iCore5-430M-JDK1.7.0_11: 60 bytes input, 100000 loops -> accepted MaybeWeCouldStealAva answer=90ms. DatatypeConverter.printHexBinary=190ms.Adopt
DatatypeConverter is no longer accessible as of JDK 9Billman
@Billman Why do you said it? At least in API docs, it's still there: docs.oracle.com/javase/9/docs/api/javax/xml/bind/…Abcoulomb
@Abcoulomb It is still there, but not part of the default runtime (due to Java 9 modules).Gibbeon
don't rely on javax.xml.bind, it compiles fine but can be not be found at runtime. if you do, be prepared to handle java.lang.NoClassDefFoundErrorShoal
A proper solution inside a Java EE project with Jaxb provided.Gintz
L
291

Simplest solution, no external libs, no digits constants:

public static String byteArrayToHex(byte[] a) {
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a)
      sb.append(String.format("%02x", b));
   return sb.toString();
}
Lukash answered 22/10, 2012 at 7:38 Comment(8)
This is very slow, on average 1000 times slower (for 162 bytes long) than the one in the top response. Avoid using String.Format if performance matters.Blasphemous
Maybe slow. It's good for things happening occasionally, such as login or similar.Lukash
Not "maybe slow", but very very inefficient. If you want to use slow code, at least reuse (not copy+paste) some of an existing library that has a test suite.Demi
If it's slow, so what? In my use case it's just for a debug statement, so thanks for this code fragment.Victorious
Reusing a library by including an extra JAR files of several dozens kB would not exactly be efficient if all you need is this function (on some platforms like Android, the whole Jar gets included in the end application). And sometimes shorter and more clear code is better when performance is not needed.Odisodium
@Odisodium maybe, but in that case you need stream support, not a single call feature. this one is easy to understand and remember, and therefore to maintain.Remittee
Creating a new java.util.Formatter for every single byte is not good for performance. It’s better to create a single Formatter (note that new Formatter() automatically wraps a new StringBuilder internally), and call its format method in the loop.Prochora
how did you come up with that "1000 times slower"?Eudosia
C
191

Here are some common options ordered from simple (one-liner) to complex (huge library). If you are interested in performance, see the micro benchmarks below.

Option 1: Code snippet - Simple (only using JDK/Android)

Option 1a: BigInteger

One very simple solution is to use the BigInteger's hex representation:

new BigInteger(1, someByteArray).toString(16);

Note that since this handles numbers not arbitrary byte-strings it will omit leading zeros - this may or may not be what you want (e.g. 000AE3 vs 0AE3 for a 3 byte input). This is also very slow, about 100x slower compared to option 2.

Option 1b: String.format()

Using the %X placeholder, String.format() is able to encode most primitive types (short, int, long) to hex:

String.format("%X", ByteBuffer.wrap(eightByteArray).getLong());

Option 1c: Integer/Long (only 4/8 Byte Arrays)

If you exclusively have 4 bytes arrays you can use the toHexString method of the Integer class:

Integer.toHexString(ByteBuffer.wrap(fourByteArray).getInt());

The same works with 8 byte arrays and Long

Long.toHexString(ByteBuffer.wrap(eightByteArray).getLong());

Option 1d: JDK17+ HexFormat

Finally, JDK 17 offers first-level support of straight forward hex encoding with HexFormat:

HexFormat hex = HexFormat.of();
hex.formatHex(someByteArray)

Option 2: Code snippet - Advanced

Here is a full-featured, copy & pasteable code snippet supporting upper/lowercase and endianness. It is optimized to minimize memory complexity and maximize performance and should be compatible with all modern Java versions (5+).

private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
        
public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {

    // our output size will be exactly 2x byte-array length
    final char[] buffer = new char[byteArray.length * 2];

    // choose lower or uppercase lookup table
    final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;

    int index;
    for (int i = 0; i < byteArray.length; i++) {
        // for little endian we count from last to first
        index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;
        
        // extract the upper 4 bit and look up char (0-A)
        buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
        // extract the lower 4 bit and look up char (0-A)
        buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
    }
    return new String(buffer);
}

public static String encode(byte[] byteArray) {
    return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}

The full source code with Apache v2 license and decoder can be found here.

Option 3: Using a small optimized library: bytes-java

While working on my previous project, I created this little toolkit for working with bytes in Java. It has no external dependencies and is compatible with Java 7+. It includes, among others, a very fast and well tested HEX en/decoder:

import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()

You can check it out on GitHub: bytes-java.

Option 4: Apache Commons Codec

Of course there is the good 'ol commons codecs. (warning opinion ahead) While working on the project outlined above I analyzed the code and was quite disappointed; a lot of duplicate unorganized code, obsolete and exotic codecs probably only useful for very few and quite overengineered and slow implementations of popular codecs (specifically Base64). I therefore would make an informed decision if you want to use it or an alternative. Anyways, if you still want to use it, here is a code snippet:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));

Option 5: Google Guava

More often than not you already have Guava as a dependency. If so just use:

import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);

Option 6: Spring Security

If you use the Spring framework with Spring Security you can use the following:

import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));

Option 7: Bouncy Castle

If you already use the security framework Bouncy Castle you can use its Hex util:

import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);

Not Really Option 8: Java 9+ Compatibility or 'Do Not Use JAXBs javax/xml/bind/DatatypeConverter'

In previous Java (8 and below) versions the Java code for JAXB was included as runtime dependency. Since Java 9 and Jigsaw modularisation your code cannot access other code outside of its module without explicit declaration. So be aware if you get an exception like:

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

when running on a JVM with Java 9+. If so then switch implementations to any of the alternatives above. See also this question.


Micro Benchmarks

Here are results from a simple JMH micro benchmark encoding byte arrays of different sizes. The values are operations per second, so higher is better. Note that micro benchmarks very often do not represent real world behavior, so take these results with a grain of salt.

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger     |  2,088,514 |  1,008,357 |   133,665 |       4 |
| Opt2/3: Bytes Lib    | 20,423,170 | 16,049,841 | 6,685,522 |     825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 |     529 |
| Opt5: Guava          | 10,177,925 |  6,937,833 | 2,094,658 |     257 |
| Opt6: Spring         | 18,704,986 | 13,643,374 | 4,904,805 |     601 |
| Opt7: BC             |  7,501,666 |  3,674,422 | 1,077,236 |     152 |
| Opt8: JAX-B          | 13,497,736 |  8,312,834 | 2,590,940 |     346 |

Specs: JDK 8u202, i7-7700K, Win10, 24 GB Ram. See the full benchmark here.

Benchmark Update 2022

Here are results with current JMH 1.36, Java 17 and a higher end computer

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1a: BigInteger    |  2,941,403 |  1,389,448 |   242,096 |       5 |
| Opt1d: HexFormat     | 32,635,184 | 20,262,332 | 7,388,135 |     922 |
| Opt2/3: Bytes Lib    | 31,724,981 | 22,786,906 | 6,197,028 |     930 |

Specs: JDK temurin 17.0.6, Ryzen 5900X, Win11, 24 GB DDR4 Ram

Carin answered 26/9, 2019 at 13:36 Comment(3)
Quite thorough! Since you're using Java 17, you might want to add HexFormat.of to the options for people using JDK17+.Banderole
Was not aware of that, thanks for the hint - will update the answer!Carin
@Carin Would you please let the HexFormat kick into the Benchmark?Hippogriff
B
65

A Guava solution, for completeness:

import com.google.common.io.BaseEncoding;
...
byte[] bytes = "Hello world".getBytes(StandardCharsets.UTF_8);
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);

Now hex is "48656c6c6f20776f726c64".

Bakery answered 7/4, 2016 at 13:34 Comment(2)
In Guava you can also use new HashCode(bytes).toString().Scene
As of Guava 22.0 it is HashCode.fromBytes(checksum).toString()Wayne
S
47

This simple oneliner works for me
String result = new BigInteger(1, inputBytes).toString(16);
EDIT - Using this will remove the leading zeros, but hey worked for my use-case. Thanks @Voicu for pointing it out

Scourings answered 4/11, 2012 at 15:30 Comment(2)
This oneliner drops leading zero bytes.Colander
@Colander ... And it will add a leading zero 50% of the time.Remittee
J
42

Java 17 finally contains HexFormat class so you can simply do:

HexFormat.of().formatHex(bytes);

It supports configuration as lowercase/uppercase, delimiters, prefix, suffix etc.

Jaffa answered 18/9, 2021 at 6:37 Comment(1)
Finally, something that doesn't need an external library or is a broken solutionSiouxie
D
29

I would use something like this for fixed length, like hashes:

md5sum = String.format("%032x", new BigInteger(1, md.digest()));
Dalston answered 22/1, 2017 at 6:0 Comment(1)
Thank you, this is so pithy and appropriate.Parlin
J
24

Use DataTypeConverter classjavax.xml.bind.DataTypeConverter

String hexString = DatatypeConverter.printHexBinary(bytes[] raw);

Jarvey answered 12/5, 2016 at 13:43 Comment(1)
Class removed in Java 11. See: JEP 320: Remove the Java EE and CORBA ModulesCsch
K
22

I found three different ways here: http://www.rgagnon.com/javadetails/java-0596.html

The most elegant one, as he also notes, I think is this one:

static final String HEXES = "0123456789ABCDEF";
public static String getHex( byte [] raw ) {
    if ( raw == null ) {
        return null;
    }
    final StringBuilder hex = new StringBuilder( 2 * raw.length );
    for ( final byte b : raw ) {
        hex.append(HEXES.charAt((b & 0xF0) >> 4))
            .append(HEXES.charAt((b & 0x0F)));
    }
    return hex.toString();
}
Kenrick answered 11/3, 2012 at 13:11 Comment(3)
Other methods were running on my 64 byte sample in 5ms, this one runs in 0ms. Probably best for lack of any other String functions like format.Kelsi
if (raw == null) return null is not fail fast. Why would you ever use a null key?Remittee
I suppose it's a habit to input validate. In this case, we prevent any Null reference exception, and leave it up to the caller to handle bad data.Kenrick
J
15

At the minor cost of storing the lookup table this implementation is simple and very fast.

 private static final char[] BYTE2HEX=(
    "000102030405060708090A0B0C0D0E0F"+
    "101112131415161718191A1B1C1D1E1F"+
    "202122232425262728292A2B2C2D2E2F"+
    "303132333435363738393A3B3C3D3E3F"+
    "404142434445464748494A4B4C4D4E4F"+
    "505152535455565758595A5B5C5D5E5F"+
    "606162636465666768696A6B6C6D6E6F"+
    "707172737475767778797A7B7C7D7E7F"+
    "808182838485868788898A8B8C8D8E8F"+
    "909192939495969798999A9B9C9D9E9F"+
    "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"+
    "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"+
    "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
    "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"+
    "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"+
    "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toCharArray();
   ; 

  public static String getHexString(byte[] bytes) {
    final int len=bytes.length;
    final char[] chars=new char[len<<1];
    int hexIndex;
    int idx=0;
    int ofs=0;
    while (ofs<len) {
      hexIndex=(bytes[ofs++] & 0xFF)<<1;
      chars[idx++]=BYTE2HEX[hexIndex++];
      chars[idx++]=BYTE2HEX[hexIndex];
    }
    return new String(chars);
  }
Joleenjolene answered 29/1, 2014 at 11:35 Comment(4)
Why not initialize the BYTE2HEX array with a simple for cycle?Cupronickel
@Cupronickel Is that even possible with a static final (aka constant) field?Andrien
@Andrien It can be assigned in a static { } block.Olivette
@Cupronickel because its faster to hardcode a lookup table than to generate it. Here memory complexity is traded with time complexity, ie. needs more memory but faster (every so slightly on both ends)Carin
S
11

HexFormat was added in Java 17:

String hex = HexFormat.of().formatHex(array);
Sixgun answered 3/11, 2021 at 14:55 Comment(0)
O
9

We don't need to use any external library or to write code based on loops and constants.
Is enough just this:

byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);
Objectify answered 10/4, 2020 at 18:3 Comment(2)
This is very similar to everconfusedGuy's Answer.Pris
Careful you may end up skipping prefix characters in hashes that start with 0's because of how it's first stored as a BigIntegerSplatter
G
8

How about this?

    String byteToHex(final byte[] hash)
    {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }
Garlic answered 21/4, 2015 at 22:44 Comment(0)
S
6

Here's yet another method using Streams:

private static String toHexString(byte[] bytes) {
    return IntStream.range(0, bytes.length)
    .mapToObj(i -> String.format("%02X", bytes[i]))
    .collect(Collectors.joining());
}
Schober answered 2/12, 2020 at 17:46 Comment(0)
W
4
public static String toHexString(byte[] bytes) {

    StringBuilder sb = new StringBuilder();

    if (bytes != null) 
        for (byte b:bytes) {

            final String hexString = Integer.toHexString(b & 0xff);

            if(hexString.length()==1)
                sb.append('0');

            sb.append(hexString);//.append(' ');
        }

      return sb.toString();//.toUpperCase();
}

To use DatatypeConverter:

public String toHexString(byte... bytes) {

    return Optional.ofNullable(bytes)
            .filter(bs->bs.length>0)
            .map(DatatypeConverter::printHexBinary)
            .map(str->IntStream.range(0, str.length())
                    .filter(i->(i%2)==0)        // take every second index
                    .mapToObj(i->"0x" + str.substring(i, i+2))
                    .collect(Collectors.joining(" ")))
            .orElse("");
}
Wheelhorse answered 8/1, 2021 at 14:33 Comment(0)
P
3

Adding a utility jar for simple function is not good option. Instead assemble your own utility classes. following is possible faster implementation.

public class ByteHex {

    public static int hexToByte(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 String[] byteToHexTable = new String[]
    {
        "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"
    };

    private static final String[] byteToHexTableLowerCase = new String[]
    {
        "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"
    };

    public static String byteToHex(byte b){
        return byteToHexTable[b & 0xFF];
    }

    public static String byteToHex(byte[] bytes){
        if(bytes == null) return null;
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTable[b & 0xFF]);
        return sb.toString();
    }

    public static String byteToHex(short[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(short b : bytes) sb.append(byteToHexTable[((byte)b) & 0xFF]);
        return sb.toString();
    }

    public static String byteToHexLowerCase(byte[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTableLowerCase[b & 0xFF]);
        return sb.toString();
    }

    public static byte[] hexToByte(String hexString) {
        if(hexString == null) return null;
        byte[] byteArray = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            byteArray[i / 2] = (byte) (hexToByte(hexString.charAt(i)) * 16 + hexToByte(hexString.charAt(i+1)));
        }
        return byteArray;
    }

    public static byte hexPairToByte(char ch1, char ch2) {
        return (byte) (hexToByte(ch1) * 16 + hexToByte(ch2));
    }


}
Piebald answered 19/6, 2019 at 13:1 Comment(2)
It is never a good choice to write your own "wheel", given there are implementations already available.Ledeen
@DonaldDuck I agree with you in most cases. But sometimes adding a dependency to just use one function is not good either.Piebald
B
2

Ok so there are a bunch of ways to do this, but if you decide to use a library I would suggest poking about in your project to see if something has been implemented in a library that is already part of your project before adding a new library just to do this. For example if you don't already have

org.apache.commons.codec.binary.Hex

maybe you do have...

org.apache.xerces.impl.dv.util.HexBin

Boson answered 5/9, 2016 at 20:15 Comment(0)
S
2

If you're using the Spring Security framework, you can use:

import org.springframework.security.crypto.codec.Hex

final String testString = "Test String";
final byte[] byteArray = testString.getBytes();
System.out.println(Hex.encode(byteArray));
Sob answered 13/2, 2019 at 17:51 Comment(0)
H
1

I usually use the following method for debuf statement, but i don't know if it is the best way of doing it or not

private static String digits = "0123456789abcdef";

public static String toHex(byte[] data){
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i != data.length; i++)
    {
        int v = data[i] & 0xff;
        buf.append(digits.charAt(v >> 4));
        buf.append(digits.charAt(v & 0xf));
    }
    return buf.toString();
}
Hidalgo answered 21/4, 2014 at 15:48 Comment(1)
If your debuffer has a bad day, try cluing in StringBuilder instantiation with a number of chars to support: StringBuilder buf = new StringBuilder(data.length * 2);.Micromho
H
1

I prefer to use this:

final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes, int offset, int count) {
    char[] hexChars = new char[count * 2];
    for ( int j = 0; j < count; j++ ) {
        int v = bytes[j+offset] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

It is slightly more flexible adaptation of the accepted answer. Personally, I keep both the accepted answer and this overload along with it, usable in more contexts.

Hysterics answered 17/6, 2014 at 15:23 Comment(1)
The original question was for byte[] to String. Look hex to bytes[] or ask a different question, @NonExistent.Hysterics
F
1

Recently I had to implement a Hex converter to dump the byte stream into the log in Hex format. Initially I did it using Hex.encodeHex which has been already discussed in here.

But If you want to represent the byte array in a very presentable/readable way io.netty.buffer library could be a great use as it prints out the Hex as well the strings in it eliminating the non-printable characters.

Requirement was something like,

0010   56 56 09 35 32 f0 b2 00 50 4c 45 41 53 45 20 52   VV.52...PLEASE R
0020   45 2d 45 4e 54 45 52 20 4c 41 53 54 20 54 52 41   E-ENTER LAST TRA
0030   4e 53 41 43 54 49 4f 4e 00 04                     NSACTION..

The shortest way to do the same in a more presentable way using io.netty.buffer is

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;

void hexDump(byte[] buf) {
    ByteBuf byteBuf = Unpooled.wrappedBuffer(buf);
    log.trace("Bytes received (Hex)\n" + ByteBufUtil.prettyHexDump(byteBuf.slice()));
}

if you are using maven, include the below dependency in the pom.xml (check for the latest version in the netty page)

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-buffer</artifactId>
    <version>4.1.68.Final</version>
</dependency>

output was:

         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000010| 40 40 b3 f3 80 f3 80 f3 80 f1 48 f1 41 f1 4e f1 |@@........H.A.N.|
|00000020| 47 f1 49 f1 4e f1 47 b5 f1 52 f1 4f f1 43 f1 4b |G.I.N.G..R.O.C.K|
|00000030| f3 80 f3 80 41 b4 40 40 f3 80 f3 80 40 f3 80 04 |....A.@@....@...|
+--------+-------------------------------------------------+----------------+

For your reference, the long way (may not be the most efficient) using the discussed methods in the answers is,

public static String hexDump(byte[] buf) throws DecoderException
{
    ByteBuffer byteBuf = ByteBuffer.wrap(buf);
    char[] result = Hex.encodeHex(byteBuf);

    String bin = new String(result).toUpperCase();
    String str = new String(Hex.decodeHex(bin), StandardCharsets.UTF_8);

    str = str.replaceAll("[^!-~]", ".");
    StringBuilder out = new StringBuilder();
    int bytes_per_line = 16;

    for (int pos = 0; pos < str.length(); pos += bytes_per_line) {
        out.append(String.format("%04X   ", pos));
        if (2 * (pos + bytes_per_line) >= bin.length()) {
            out.append(String.format("%-" + 2 * bytes_per_line + "s", bin.substring(2 * pos)).replaceAll("..", "$0 "));
        } else {
            out.append(bin.substring(2 * pos, 2 * (pos + bytes_per_line)).replaceAll("..", "$0 "));
        }
        out.append("   ");
        if (pos + bytes_per_line > str.length()) {
            out.append(str.substring(pos));

        } else {
            out.append(str.substring(pos, pos + bytes_per_line));
        }
        out.append("\n");
    }

    return out.toString();
}
Foulard answered 14/10, 2021 at 0:46 Comment(0)
C
1

If you want to make it more readable and separate the bytes from each other, you can use the following code in Java 17+:

Update: As @BradHards mentioned, it can be simplified.

byte[] yourByteArray = { -128, 0, 127 };
// String hexString = new String(HexFormat.ofDelimiter(" ").formatHex(yourByteArray));
String hexString = HexFormat.ofDelimiter(" ").formatHex(yourByteArray);
// 80 00 7f
Caboose answered 11/10, 2022 at 8:56 Comment(1)
Why the new String(..) constructor? formatHex will return a String.Romeoromeon
E
0

A small variant of the solution proposed by @maybewecouldstealavan, which lets you visually bundle N bytes together in the output hex string:

 final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
 final static char BUNDLE_SEP = ' ';

public static String bytesToHexString(byte[] bytes, int bundleSize /*[bytes]*/]) {
        char[] hexChars = new char[(bytes.length * 2) + (bytes.length / bundleSize)];
        for (int j = 0, k = 1; j < bytes.length; j++, k++) {
                int v = bytes[j] & 0xFF;
                int start = (j * 2) + j/bundleSize;

                hexChars[start] = HEX_ARRAY[v >>> 4];
                hexChars[start + 1] = HEX_ARRAY[v & 0x0F];

                if ((k % bundleSize) == 0) {
                        hexChars[start + 2] = BUNDLE_SEP;
                }   
        }   
        return new String(hexChars).trim();    
}

That is:

bytesToHexString("..DOOM..".toCharArray().getBytes(), 2);
2E2E 444F 4F4D 2E2E

bytesToHexString("..DOOM..".toCharArray().getBytes(), 4);
2E2E444F 4F4D2E2E
Eph answered 4/3, 2015 at 12:39 Comment(0)
N
0
  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;
  } 
Naivete answered 9/11, 2017 at 16:21 Comment(0)
S
0

Here is a java.util.Base64-like implementation, isn't it pretty?

import java.util.Arrays;

public class Base16/* a.k.a. Hex */ {
    public static class Encoder{
        private static char[] toLowerHex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        private static char[] toUpperHex={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        private boolean upper;
        public Encoder(boolean upper) {
            this.upper=upper;
        }
        public String encode(byte[] data){
            char[] value=new char[data.length*2];
            char[] toHex=upper?toUpperHex:toLowerHex;
            for(int i=0,j=0; i<data.length; i++){
                int octet=data[i]&0xFF;
                value[j++]=toHex[octet>>4];
                value[j++]=toHex[octet&0xF];
            }
            return new String(value);
        }
        static final Encoder LOWER_CASE=new Encoder(false);
        static final Encoder UPPER_CASE=new Encoder(true);
    }
    public static Encoder getEncoder(){
        return Encoder.LOWER_CASE;
    }
    public static Encoder getUpperEncoder(){
        return Encoder.UPPER_CASE;
    }

    public static class Decoder{
      private static int maxIndex=102;
      private static int[] toIndex;
      static {
        toIndex=new int[maxIndex+1];
        Arrays.fill(toIndex, -1);
        char[] chars={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','a','b','c','d','e','f'};
        for(int i=0; i<chars.length; i++) {
          toIndex[(int)chars[i]]=i;
        }
      }
      public Decoder() {
      }
      public byte[] decode(String str) {
          char[] value=str.toCharArray();
          int start=0;
          if(value.length>2 && value[0]=='0' && (value[1]=='x' || value[1]=='X')) {
            start=2;
          }
          int byteLength=(value.length-start)/2; // ignore trailing odd char if exists
          byte[] data=new byte[byteLength];
          for(int i=start,j=0;i<value.length;i+=2,j++){
              int i1;
              int i2;
              char c1=value[i];
              char c2=value[i+1];
              if(c1>maxIndex || (i1=toIndex[(int)c1])<0 || c2>maxIndex || (i2=toIndex[(int)c2])<0) {
                throw new IllegalArgumentException("Invalid character at "+i);
              }
              data[j]=(byte)((i1<<4)+i2);
          }
          return data;
      }
      static final Decoder IGNORE_CASE=new Decoder();
  }
  public static Decoder getDecoder(){
      return Decoder.IGNORE_CASE;
  }
}
Sling answered 3/12, 2017 at 13:7 Comment(0)
S
0
private static String bytesToHexString(byte[] bytes, int length) {
        if (bytes == null || length == 0) return null;

        StringBuilder ret = new StringBuilder(2*length);

        for (int i = 0 ; i < length ; i++) {
            int b;

            b = 0x0f & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));

            b = 0x0f & bytes[i];
            ret.append("0123456789abcdef".charAt(b));
        }

        return ret.toString();
    }
Swoop answered 20/2, 2018 at 4:19 Comment(0)
S
0

Can't find any solution on this page that doesn't

  1. Use a loop
  2. Use javax.xml.bind.DatatypeConverter which compiles fine but often throws java.lang.NoClassDefFoundError at runtime.

Here's a solution which doesn't have the flaws above(no promises mine doesn't have other flaws though)

import java.math.BigInteger;

import static java.lang.System.out;
public final class App2 {
    // | proposed solution.
    public static String encode(byte[] bytes) {          
        final int length = bytes.length;

        // | BigInteger constructor throws if it is given an empty array.
        if (length == 0) {
            return "00";
        }

        final int evenLength = (int)(2 * Math.ceil(length / 2.0));
        final String format = "%0" + evenLength + "x";         
        final String result = String.format (format, new BigInteger(bytes));

        return result;
    }

    public static void main(String[] args) throws Exception {
        // 00
        out.println(encode(new byte[] {})); 

        // 01
        out.println(encode(new byte[] {1})); 

        //203040
        out.println(encode(new byte[] {0x20, 0x30, 0x40})); 

        // 416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e
        out.println(encode("All your base are belong to us.".getBytes()));
    }
}   

I couldn't get this under 62 opcodes, but if you can live without 0 padding in case the first byte is less than 0x10, then the following solution only uses 23 opcodes. Really shows how "easy to implement yourself" solutions like "pad with a zero if string length is odd" can get pretty expensive if a native implementation is not already available(or in this case, if BigInteger had an option to prefix with zeros in toString).

public static String encode(byte[] bytes) {          
    final int length = bytes.length;

    // | BigInteger constructor throws if it is given an empty array.
    if (length == 0) {
        return "00";
    }

    return new BigInteger(bytes).toString(16);
}
Shoal answered 14/5, 2018 at 23:21 Comment(0)
B
0

My solution is based on maybeWeCouldStealAVan's solution, but does not rely on any additionaly allocated lookup tables. It does not uses any 'int-to-char' casts hacks (actually, Character.forDigit() does it, performing some comparison to check what the digit truly is) and thus might be a bit slower. Please feel free to use it wherever you want. Cheers.

public static String bytesToHex(final byte[] bytes)
{
    final int numBytes = bytes.length;
    final char[] container = new char[numBytes * 2];

    for (int i = 0; i < numBytes; i++)
    {
        final int b = bytes[i] & 0xFF;

        container[i * 2] = Character.forDigit(b >>> 4, 0x10);
        container[i * 2 + 1] = Character.forDigit(b & 0xF, 0x10);
    }

    return new String(container);
}
Becalm answered 2/6, 2018 at 22:33 Comment(0)
T
0
Converts bytes data to hex characters

@param bytes byte array to be converted to hex string
@return byte String in hex format

private static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for (int j = 0; j < bytes.length; j++) {
        v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}
Thalassic answered 30/4, 2019 at 7:9 Comment(0)
C
-1

If you're looking for a byte array exactly like this for python, I have converted this Java implementation into python.

class ByteArray:

@classmethod
def char(cls, args=[]):
    cls.hexArray = "0123456789ABCDEF".encode('utf-16')
    j = 0
    length = (cls.hexArray)

    if j < length:
        v = j & 0xFF
        hexChars = [None, None]
        hexChars[j * 2] = str( cls.hexArray) + str(v)
        hexChars[j * 2 + 1] = str(cls.hexArray) + str(v) + str(0x0F)
        # Use if you want...
        #hexChars.pop()

    return str(hexChars)

array = ByteArray()
print array.char(args=[])
Contamination answered 13/11, 2015 at 15:42 Comment(0)
J
-2

// Shifting bytes is more efficient // You can use this one too

public static String getHexString (String s) 
{
    byte[] buf = s.getBytes();

    StringBuffer sb = new StringBuffer();

    for (byte b:buf)
    {
        sb.append(String.format("%x", b));
    }


        return sb.toString();
}
Jinnyjinrikisha answered 26/4, 2013 at 4:31 Comment(2)
Be aware that this approach produces single character values for bytes 0 to 15 (i.e. 0..F). The expectation would probably be getting 2 character values 00 .. 0F. At least converting back from hex to bytes[] won't work.Rhinestone
You should replace "%x" by "%02x" as @Rhinestone pointed outCaboose
J
-3

Just iterate over all bytes, convert them to a hex String using Integer.toString (Unfortunately, there is no Byte.toString with a radix parameter) and append them all to a StringBuilder.

byte[] arr;//set it to your value
StringBuilder sb=new StringBuilder(arr.length*2);//1 byte...2 hex digits
for(int i=0;i<arr.length;i++){
  sb.append(Integer.toString(arr[i],16));
}
String hexValue=sb.toString();

This is similar to this answer from Pointer Null but it uses Integer.toString instead of String.format improving the performance.

Jazminejazz answered 26/12, 2021 at 17:29 Comment(1)
This is wrong. The above does not take into account that the Integer.toString(x,16) method does not left-pad with 0. Most people would probably expect that a String with value A\nB would be represented as 410a42 in hex. However the presented solution will give 41a42. Ouch!Sang

© 2022 - 2024 — McMap. All rights reserved.