Найти в Дзене
Уроки по программированию

Преобразование массивов байт в шестнадцатеричные строки в Kotlin

В этом руководстве мы рассмотрим несколько способов преобразования ByteArray в шестнадцатеричную строку в Kotlin. Сначала мы разберём общий алгоритм преобразования. Затем реализуем его с помощью стандартных библиотек Kotlin и Java. И в качестве бонуса — покажем реализацию с использованием циклов и побитовых операций. Чтобы преобразовать массив байтов в его шестнадцатеричный эквивалент, нужно: Поскольку 1 байт = 8 бит, а 1 шестнадцатеричный символ = 4 бита, каждый байт должен быть представлен двумя символами.
Если полученное значение содержит только один символ — добавляем ведущий ноль (0), чтобы не потерять информацию при декодировании. Теперь, зная общий принцип, реализуем его в Kotlin. fun ByteArray.toHex(): String =
joinToString(separator = "") { eachByte -> "%02x".format(eachByte) } Пример проверки: val md5 = MessageDigest.getInstance("md5")
md5.update("baeldung".toByteArray())
val digest: ByteArray = md5.digest()
assertEquals("9d2ea3506b1121365e5eec24c92c528d", digest.toHe
Оглавление

1. Введение

В этом руководстве мы рассмотрим несколько способов преобразования ByteArray в шестнадцатеричную строку в Kotlin.

Сначала мы разберём общий алгоритм преобразования. Затем реализуем его с помощью стандартных библиотек Kotlin и Java. И в качестве бонуса — покажем реализацию с использованием циклов и побитовых операций.

2. Алгоритм

Чтобы преобразовать массив байтов в его шестнадцатеричный эквивалент, нужно:

  1. Преобразовать беззнаковое значение каждого байта в соответствующее шестнадцатеричное значение.
  2. Объединить полученные значения в одну строку.

Поскольку 1 байт = 8 бит, а 1 шестнадцатеричный символ = 4 бита, каждый байт должен быть представлен двумя символами.

Если полученное значение содержит только один символ — добавляем ведущий ноль (0), чтобы не потерять информацию при декодировании.

Теперь, зная общий принцип, реализуем его в Kotlin.

3. Стандартная библиотека Kotlin

3.1. С использованием joinToString и String.format

fun ByteArray.toHex(): String =
joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
  • %02x — форматная строка, которая преобразует байт в шестнадцатеричное значение и дополняет ноль слева, если нужно.
  • joinToString("") — объединяет все значения в одну строку без разделителей.

Пример проверки:

val md5 = MessageDigest.getInstance("md5")
md5.update("baeldung".toByteArray())

val digest: ByteArray = md5.digest()
assertEquals("9d2ea3506b1121365e5eec24c92c528d", digest.toHex())

3.2. С использованием беззнаковых типов (Kotlin 1.3+)

@ExperimentalUnsignedTypes
fun ByteArray.toHex2(): String =
asUByteArray().joinToString("") { it.toString(radix = 16).padStart(2, '0') }
  • asUByteArray() — преобразует байты в беззнаковый массив.
  • toString(radix = 16) — преобразует байт в строку в 16-ричной системе.
  • padStart(2, '0') — добавляет ведущий ноль при необходимости.
Беззнаковые типы стабильны с Kotlin 1.5, но массивы UByteArray всё ещё требуют аннотацию @ExperimentalUnsignedTypes.

3.3. Через Java API (без экспериментальных типов)

fun ByteArray.toHex3(): String =
joinToString("") {
java.lang.Byte.toUnsignedInt(it).toString(16).padStart(2, '0')
}
  • toUnsignedInt(it) — получает беззнаковое значение байта.
  • Преобразует его в строку и дополняет ведущим нулём.

4. Java 17

Начиная с Java 17 (на момент написания — в стадии предварительного доступа), в стандартной библиотеке появилась утилитная функция java.util.HexFormat, которая стала идиоматическим способом для преобразования массивов байт в шестнадцатеричные строки и обратно.

Если вы используете Java 17 и выше, то рекомендуется использовать эту утилиту вместо ручных реализаций:

val hex = HexFormat.of().formatHex(digest)
assertEquals("9d2ea3506b1121365e5eec24c92c528d", hex)

HexFormat — это удобная абстракция с мощным и простым API для работы с байтами и шестнадцатеричными строками.

5. Циклы и побитовые операции

Как и обещали, теперь реализуем преобразование вручную, без использования joinToString или библиотек — только циклы и побитовые операции:

val hexChars = "0123456789abcdef".toCharArray()

fun ByteArray.toHex4(): String {
val hex = CharArray(2 * this.size)
this.forEachIndexed { i, byte ->
val unsigned = 0xff and byte.toInt()
hex[2 * i] = hexChars[unsigned / 16]
hex[2 * i + 1] = hexChars[unsigned % 16]
}
return hex.joinToString("")
}

Каждый байт можно представить двумя шестнадцатеричными символами. Поэтому мы создаём CharArray, размер которого в два раза превышает размер входного ByteArray.

Затем мы проходимся по массиву байт. В каждой итерации сначала получаем беззнаковое значение текущего байта. Для этого мы выполняем побитовую операцию AND между значением байта и 0xff.

Так как шестнадцатеричные значения всегда положительные (их старший бит равен нулю), 0xff обнуляет знаковый бит у отрицательных чисел, сохраняя положительные значения неизменными:

-2

На приведённой выше схеме видно, как результат выражения -1 & 0xff оказывается равным 255.

При делении этого значения на 16:

  • частное определяет первый шестнадцатеричный символ,
  • остатоквторой символ.

Именно поэтому мы заполняем элементы массива CharArray с помощью операций деления и остатка от деления:

-3

Можно было бы подумать, что замена операторов деления / и остатка от деления % на их побитовые аналоги улучшит производительность. Однако современные компиляторы JVM, такие как C2, как правило, автоматически применяют такие оптимизации во время выполнения. Так что не будем жертвовать читаемостью ради мнимой выгоды!

6. Заключение

В этом руководстве мы рассмотрели различные способы преобразования массивов байт в их шестнадцатеричное представление.

Чтобы лучше понять принцип работы каждого подхода, мы начали с простого описания алгоритма, а затем реализовали его через:

  • стандартные функции Kotlin (joinToString, format, padStart)
  • Java API (toUnsignedInt, HexFormat)
  • ручную реализацию через циклы и побитовые операции

Таким образом, вы можете выбрать наиболее удобный или производительный метод для своих задач.

Оригинал статьи: https://www.baeldung.com/kotlin/byte-arrays-to-hex-strings