1. 简介
十六进制字符串在编程中常用于以可读形式表示二进制数据。因此,在处理底层数据、加密算法或网络协议时,经常需要将十六进制字符串转换为字节数组。幸运的是,Kotlin 提供了多种高效的方式来完成这一转换。
本文将介绍几种在 Kotlin 中将十六进制字符串转换为字节数组的常用方法,帮助你在实际开发中灵活选择合适方案,避免踩坑。
2. 使用 for 循环手动实现
最直观的方式是通过 for
循环逐对解析十六进制字符。这种方式虽然代码稍多,但逻辑清晰,适合理解底层原理。
fun hexStringToByteArrayCustom(hex: String): ByteArray {
val length = hex.length
val byteArray = ByteArray(length / 2)
for (i in byteArray.indices) {
val index = i * 2
val byte = hex.substring(index, index + 2).toInt(16).toByte()
byteArray[i] = byte
}
return byteArray
}
✅ 说明:
- 创建长度为原字符串一半的
ByteArray
(每两个 hex 字符对应一个字节) - 遍历每个索引,提取两个字符组成的子串
- 使用
toInt(16)
将其解析为十进制整数,再转为Byte
⚠️ 注意: 此方法假设输入字符串长度为偶数且仅包含合法十六进制字符,否则会抛出异常。生产环境需加校验。
测试用例验证正确性:
@Test
fun `convert hex string to byte array using for loop`() {
val hexString = "48656C6C6F20576F726C64"
val expectedByteArray = byteArrayOf(72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100)
assertArrayEquals(expectedByteArray, hexStringToByteArrayUsingForLoop(hexString))
}
输出对应 ASCII 字符串 "Hello World"
,结果正确。
3. 使用标准库方法(chunked + map)
Kotlin 标准库提供了函数式风格的简洁写法,结合 chunked(2)
和 map
可一行搞定。
fun hexStringToByteArrayUsingStandardLibraryMethods(hex: String): ByteArray {
return hex.chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
✅ 优点:
- 代码简洁,易读
- 利用了 Kotlin 强大的集合操作 API
❌ 缺点:
chunked
会创建中间列表,对超长字符串有轻微性能开销
测试验证:
@Test
fun `convert hex string to byte array using standard libraries methods`() {
val hexString = "48656C6C6F20576F726C64"
val expectedByteArray = byteArrayOf(72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100)
assertArrayEquals(expectedByteArray, hexStringToByteArrayUsingStandardLibraryMethods(hexString))
}
4. 使用 hexToByteArray()(实验性 API)
Kotlin 在 kotlin.text
包中提供了一个实验性的扩展函数 hexToByteArray()
,可以直接调用。
@OptIn(ExperimentalStdlibApi::class)
@Test
fun `convert hex string to byte array using hexToByteArray`() {
val hexString = "48656C6C6F20576F726C64"
val expectedByteArray = byteArrayOf(72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100)
assertArrayEquals(expectedByteArray, hexString.hexToByteArray())
}
⚠️ 关键点:
- 必须添加
@OptIn(ExperimentalStdlibApi::class)
注解才能使用 - 属于实验性功能,API 不稳定,不建议在生产环境直接依赖
- 内部实现做了输入校验和大小写兼容,相对安全
📌 建议仅用于原型或学习场景,正式项目慎用。
5. 使用 BigInteger 转换
对于非常长的十六进制字符串(如大数运算场景),可以借助 java.math.BigInteger
来处理。
fun hexStringToByteArrayUsingBigInteger(hexString: String): ByteArray {
val bigInteger = BigInteger(hexString, 16)
return bigInteger.toByteArray()
}
✅ 适用场景:
- 处理超长 hex 字符串(如哈希值、公钥等)
- 已有
BigInteger
上下文,无需额外引入依赖
⚠️ 注意事项:
BigInteger.toByteArray()
返回的结果可能多出一个符号位(补码表示),例如"FF"
会变成[0, -1]
而不是[-1]
- 若需精确匹配原始字节流,可能需要手动去除前导零或调整长度
测试示例:
@Test
fun `convert hex string to byte array using BigInteger`() {
val hexString = "48656C6C6F20576F726C64"
val expectedByteArray = byteArrayOf(72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100)
assertArrayEquals(expectedByteArray, hexStringToByteArrayUsingBigInteger(hexString))
}
虽然此例通过,但在处理 0x80
以上高位字节时需特别小心符号扩展问题。
6. 总结与选型建议
方法 | 是否推荐 | 场景 |
---|---|---|
✅ 手动 for 循环 | ⚠️ 学习可用 | 理解原理,控制细节 |
✅ chunked(2).map.toByte().toByteArray() |
✅ 推荐 | 日常开发首选,简洁清晰 |
❌ hexToByteArray() |
⚠️ 慎用 | 实验性 API,稳定性差 |
✅ BigInteger |
✅ 特定场景 | 大数、密码学相关 |
📌 最终建议:
- 一般业务场景优先使用
chunked + map
方案 - 对性能敏感且频繁调用的场景,可封装一个无中间对象的循环版本
- 注意输入合法性校验(长度为偶数、只含
0-9a-fA-F
) - 如需处理负数或固定长度字节流,注意
BigInteger
的符号位问题
所有示例代码已整理至 GitHub: https://github.com/baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-strings-5