将字节数组转换为十六进制字符串

39

令人惊讶的是(对我而言),这段代码并没有实现我想要的效果:

fun ByteArray.toHexString() : String {
    return this.joinToString("") { it.toString(16) }
}

原来的 Byte 是有符号的,所以你会得到负数的十六进制表示形式,这会导致完全错误的最终结果。

Byte.toString 也不会填充前导零,而在这种情况下您可能需要它。

最简单(不使用额外的库,理想情况下也没有扩展)和最有效的修复方法是什么?


你能给我一个输入,以便我得到虚假的输出吗? - Roland
3
@Roland 0xFF.toByte().toString(16) -> 0xFF.toByte().toString(16) - Salem
@Roland 我的提示是看到 SHA-256 哈希中的“-”。 - Raphael
尝试了从 0x000xFF 的所有单字节字符串。此页面上的所有变体都产生相同的结果...我在这里漏掉了什么? - Roland
只是想知道:你如何获取你的 ByteArray - Roland
@Roland 将 UTF-8 字符串转换为 ByteArray,然后使用 BouncyCastle 分别进行哈希和加密。没有加密元素就无法复现(https://pastebin.com/4X1R3TKg)。 - Raphael
5个回答

84

由于我正在使用Kotlin 1.3版本,您可能也会对UByte感兴趣(请注意,这是一个实验性功能。另请参见Kotlin 1.3M11.3M2公告)。

例如:

@ExperimentalUnsignedTypes // just to make it clear that the experimental unsigned types are used
fun ByteArray.toHexString() = asUByteArray().joinToString("") { it.toString(16).padStart(2, '0') }

格式选项可能是其他变体中最好的(但可能不太容易阅读......而且我总是忘记它是如何工作的,所以对我来说肯定不容易记住):


格式选项可能是其他变体中最好的(但可能不太容易阅读……而且我总是忘记它的用法,所以对我来说很难记住(:-))。
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }

14
如果有人需要不针对JVM的版本,可以使用以下代码: fun ByteArray.toHexString() = joinToString("") { (0xFF and it.toInt()).toString(16).padStart(2, '0') } 该代码功能为将ByteArray转换为十六进制字符串。 - Sven

10

printf 在这里达到了我们的需求:

fun ByteArray.toHexString() : String {
    return this.joinToString("") {
        java.lang.String.format("%02x", it)
    }
}

我怀疑你需要拼写出 java.lang.String - msrd0
3
@msrd0 这个问题上没有Java标签。使用KotlinJS,我可以在第一眼就排除掉这样的答案,这点我很欣赏。 - Zackline
@msrd0 我想确保不选择 Kotlin 的 String,但似乎这是不必要的。 - Raphael
3
有趣的观点。关于标记 Kotlin 问题涉及 JVM/JS/Android/Native,是否已经出现了任何公约? - Raphael

3
这个问题已经有人回答了,但我不太喜欢它的格式。这里有一个使其更易读的内容格式……至少对我来说如此。
@JvmOverloads
fun ByteArray.toHexString(separator: CharSequence = " ",  prefix: CharSequence = "[",  postfix: CharSequence = "]") =
    this.joinToString(separator, prefix, postfix) {
        String.format("0x%02X", it)
    }

输出:

[0x10 0x22]

2
fun ByteArray.toHexString() = joinToString("") {
    Integer.toUnsignedString(java.lang.Byte.toUnsignedInt(it), 16).padStart(2, '0')
}

幸运的是,Java 在 IntegerLong 上有 toUnsignedString 方法。不幸的是,这些方法仅适用于 IntegerLong,因此您需要先将每个字节转换(使用 Byte#toUnsignedInt)。


有什么原因导致了负评?虽然现在 Kotlin 内置了更好的方法,但这种方式仍然是正确且高效的(适用于 Kotlin/JVM)。 - Salem
@Hett 我不确定你的意思,但它确实正常工作(示例链接),并且不会产生问题中描述的行为。输出中没有任何错误的 "-",而且每个字节都得到了正确的填充。 - Salem

2

如果有人在未来搜索并找到这个问题,这里是 Kotlin 1.9+ 的答案。

有一个新的实验性功能,fun ByteArray.toHexString(format: HexFormat = HexFormat.Default)

非常简单地使用它:

byteArrayOf(0x0A, 0x0B, 0x0C).toHexString() returns "0x0b0c"

你可以自定义它:
val hexFormat = HexFormat { bytes { byteSeparator = " "; upperCase = true } }
byteArrayOf(0x0A, 0x0B, 0x0C).toHexString(hexFormat) returns "0A 0B 0C"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接