Kotlin convert hex string to ByteArray

Viewed 11464

I have this string:

val s = "00b44a0bc6303782b729a7f9b44a3611b247ddf1e544f8b1420e2aae976003219175461d2bd7" +
        "6e64ba657d7c9dff6ed7b17980778ec0cbf75fc16e52463e2d784f5f20c1691f17cdc597d7a514108" +
        "0809a38c635b2a62082e310aa963ca15953894221ad54c6b30aea10f4dd88a66c55ab9c413eae49c0b" +
        "28e6a3981e0021a7dcb0759af34b095ce3efce78938f2a2bed70939ba47591b88f908db1eadf237a7a" +
        "7100ac87130b6119d7ae41b35fd27ff6021ac928273c20f0b3a01df1e6a070b8e2e93b5220ad0210400" +
        "0c0c1e82e17fd00f6ac16ef37c3b6153d348e470843a84f25473a51f040a42671cd94ffc989eb27fd42" +
        "b817f8173bfa95bdfa17a2ae22fd5c89dab2822bcc973b5b90f8fadc9b074cca8f9365b1e8994ff0bda48" +            "b1f7498cce02d4e794915f8a4208de3eaf9fbff5"

Which is hexadecimal notation of bytes, hardcoded in as string format. I need this thing as a bytearray, importantly, not the ASCII representation of it, actually the hexadecimal values that is represents.

All the kotlin methods I can find, such as:

val b = s.encodeToByteArray()

Seem to be taking the actual ASCII value of the string, and converting it to a bytearray.

How do I create a bytearray directly from the values in this string?

2 Answers

You can handle it like this:

fun String.decodeHex(): ByteArray {
    check(length % 2 == 0) { "Must have an even length" }

    return chunked(2)
        .map { it.toInt(16).toByte() }
        .toByteArray()
}
  1. Split the string into 2-character pairs, representing each byte.
  2. Parse each hex pair to their integer values.
  3. Convert the parsed Ints to Bytes.

My other answer is the simplest way, but it creates two intermediate lists - a list of strings and a list of bytes - before it creates the byte array. Here are two slightly more complex versions that are more efficient.


This version uses sequences to take advantage of lazy evaluation. It still produces a string for every byte, but uses no intermediate lists.

fun String.decodeHex(): ByteArray {
    check(length % 2 == 0) { "Must have an even length" }

    val byteIterator = chunkedSequence(2)
        .map { it.toInt(16).toByte() }
        .iterator()

    return ByteArray(length / 2) { byteIterator.next() }
}

This version uses the JDK's java.lang.Integer.parseInt function. It creates the ByteArray directly with no intermediate data-structures.

fun String.decodeHex(): ByteArray {
    check(length % 2 == 0) { "Must have an even length" }

    return ByteArray(length / 2) {
        Integer.parseInt(this, it * 2, (it + 1) * 2, 16).toByte()
    }
}
Related