1. 概述
十六进制表示法因其可读性强、表达紧凑,在二进制数据的文本化表示中被广泛使用。为了简化相关操作,Kotlin 在标准库中引入了 HexFormat API,用于将数据格式化为十六进制字符串,以及反向解析。
本文将深入探讨 HexFormat API 的核心能力,并通过实际用例帮助你避免常见“坑点”。
✅ 适用场景:日志输出、协议编解码、MAC/IP 地址处理、调试信息展示等需要二进制与文本互转的场合。
2. 核心概念
在使用前,先理解其底层设计,避免后续踩坑。
2.1. HexFormat 类
HexFormat
是 kotlin.text
包中的核心配置类,定义了整个十六进制格式化的规则集合。其结构如下:
@ExperimentalStdlibApi
@SinceKotlin("1.9")
public class HexFormat internal constructor(
val upperCase: Boolean,
val bytes: BytesHexFormat,
val number: NumberHexFormat
) {
// 其他方法和属性
}
⚠️ 注意:
- 带有
@ExperimentalStdlibApi
注解,属于实验性 API。 - **使用时必须添加
@OptIn(ExperimentalStdlibApi::class)
**,否则编译报错。
它包含三个关键属性:
upperCase
:是否使用大写字母(A-F)。bytes
:针对ByteArray
的格式化规则。number
:针对数值类型(如 Byte、Int)的格式化规则。
2.2. NumberHexFormat 与 BytesHexFormat
NumberHexFormat
控制数值转十六进制的行为:
public class NumberHexFormat internal constructor(
val prefix: String,
val suffix: String,
val removeLeadingZeros: Boolean
)
可配置项:
prefix
/suffix
:前后缀,如"0x"
或"%"
。removeLeadingZeros
:是否去除前导零,对Long
等宽类型尤其有用。
BytesHexFormat
专用于 ByteArray
的布局控制:
public class BytesHexFormat internal constructor(
val bytesPerLine: Int,
val bytesPerGroup: Int,
val groupSeparator: String,
val byteSeparator: String,
val bytePrefix: String,
val byteSuffix: String
)
常用配置:
bytesPerGroup
:每组字节数(如 MAC 地址每 1 字节一组)。groupSeparator
:组间分隔符(如":"
或"-"
)。byteSeparator
:字节内分隔符(较少用)。bytesPerLine
:每行最多字节数(用于美化长数据输出)。
掌握这些配置,就能灵活应对各种格式需求。
3. 数值转十六进制
如何将 Byte
、Int
、Long
等类型转为 hex 字符串。
3.1. 默认格式
val number: Byte = 63
assertEquals("3f", number.toHexString())
✅ 调用 toHexString()
时若未传参,会自动使用 HexFormat.Default
实例。
也可显式传入:
assertEquals("3f", number.toHexString(HexFormat.Default))
该扩展函数支持所有整型:Byte
、Short
、Int
、Long
。
3.2. 添加前缀
某些场景(如 URL 编码)需添加特定前缀:
val prefixNumberFormat = HexFormat {
number.prefix = "%"
}
使用自定义格式:
assertEquals("%3f", number.toHexString(prefixNumberFormat))
输出结果自动带上 %
前缀。
3.3. 去除前导零
对于 Long
类型,默认会补足 16 位(8 字节):
val number: Long = 63
assertEquals("000000000000003f", number.toHexString()) // ❌ 太冗长
通过配置去除前导零:
val compactFormat = HexFormat {
number {
removeLeadingZeros = true
}
}
assertEquals("3f", number.toHexString(compactFormat)) // ✅ 干净利落
这在日志或 UI 展示中非常实用。
4. 十六进制解析为数值
使用 hexTo<Type>()
系列扩展函数将 hex 字符串还原为数值。
基础用法:
assertEquals(63, "3f".hexToByte())
⚠️ 陷阱:若字符串含自定义前缀(如 %3f
),直接解析会抛 IllegalArgumentException
:
assertThrows<IllegalArgumentException> { "%3f".hexToByte() }
原因:默认使用 HexFormat.Default
,不识别 %
。
正确做法是传入对应的 HexFormat
实例:
assertEquals(63, "%3f".hexToByte(prefixNumberFormat))
✅ 只要格式匹配,即可成功解析。
5. ByteArray 转十六进制
典型用例:格式化 MAC 地址。
定义 MAC 地址专用格式:
val macAddressFormat = HexFormat {
upperCase = true // 使用大写
bytes.bytesPerGroup = 1 // 每组 1 字节
bytes.groupSeparator = ":" // 组间用冒号分隔
}
准备原始字节数组(注意负数表示):
val macAddressBytes = byteArrayOf(2, 66, -64, -117, -14, 94)
转换为 hex 字符串:
val macAddressHexValue = macAddressBytes.toHexString(macAddressFormat)
assertEquals("02:42:C0:8B:F2:5E", macAddressHexValue)
✅ 输出符合标准 MAC 地址格式。
6. 十六进制解析为 ByteArray
将格式化的 hex 字符串还原为字节数组。
val macAddressHexValue = "02:42:C0:8B:F2:5E"
关键:必须传入与格式化时相同的 HexFormat
实例,否则无法识别分隔符:
val macAddressBytes = macAddressHexValue.hexToByteArray(macAddressFormat)
验证结果:
assertArrayEquals(byteArrayOf(2, 66, -64, -117, -14, 94), macAddressBytes)
⚠️ 若传错或不传 HexFormat
,会抛 NumberFormatException
。
7. 总结
HexFormat API 提供了一套类型安全、配置灵活的十六进制编解码方案。核心要点:
- ✅ 使用
toHexString()
和hexTo<Type>()
进行格式化与解析。 - ✅ 自定义
HexFormat
实例以支持前缀、分隔符、大小写等需求。 - ⚠️ 实验性 API 需
@OptIn(ExperimentalStdlibApi::class)
。 - ⚠️ 解析时务必传入正确的
HexFormat
,否则易出错。
代码示例已上传至 GitHub:Baeldung Kotlin 教程仓库。建议集合备用。