Swift:将UInt8字节数组转换为36进制字符串的实现问询
解决UInt8数组转36进制字符串的问题
你的思路在处理短字节数组时是可行的,但像SHA1哈希这种20字节的数组,已经超出了Swift标准库中所有固定宽度整数类型的范围(比如UInt64只有8字节,UInt128也才16字节),所以没法直接转成符合BinaryInteger协议的常规类型。下面给你两种靠谱的实现方案:
方案一:用官方SwiftNumerics库的BigInt(推荐)
苹果官方的SwiftNumerics库提供了任意精度整数类型BigInt,可以轻松处理超大字节数组的进制转换,步骤很简单:
首先需要在你的项目中引入SwiftNumerics依赖(如果是Swift Package Manager,直接在Package.swift里添加即可):
dependencies: [ .package(url: "https://github.com/apple/swift-numerics", from: "1.0.0") ], targets: [ .target(name: "YourTarget", dependencies: [.product(name: "BigInt", package: "swift-numerics")]) ]
然后编写转换代码:
import BigInt // 你的UInt8数组 let bArray: [UInt8] = [169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47, 187, 211] // 转成Data类型 let data = Data(bArray) // 用大端字节序初始化BigInt(哈希值通常用大端表示) guard let bigInt = BigInt(data, byteOrder: .bigEndian) else { fatalError("Failed to create BigInt from data") } // 转成36进制字符串 let base36String = String(bigInt, radix: 36, uppercase: false) print(base36String) // 输出你预期的结果:jrwjerxiekdtj9k82lg930wpkr6tq6r
这个方案的优点是代码简洁,官方库实现的逻辑稳定可靠,几乎不用考虑边界问题。
方案二:自定义实现(无第三方依赖)
如果你的项目不能引入外部依赖,可以手动实现大整数的36进制转换逻辑。核心思路是把字节数组当作一个超大整数,反复除以36取余数,再把余数映射成对应的字符,最后反转结果:
extension [UInt8] { func toBase36(uppercase: Bool = false) -> String { guard !isEmpty else { return "" } // 定义36进制字符集:0-9 + a-z(或A-Z) let charSet = Array((0...9).map(String.init) + (0...25).map { String(UnicodeScalar($0 + (uppercase ? 65 : 97))) }) var number = self // 用数组模拟大整数,大端存储 var resultChars = [String]() // 循环除以36,直到数字变为0 while !number.allSatisfy({ $0 == 0 }) { var remainder = 0 var newNumber = [UInt8]() for byte in number { let currentValue = Int(byte) + remainder * 256 let quotient = currentValue / 36 remainder = currentValue % 36 // 避免前导零 if !newNumber.isEmpty || quotient != 0 { newNumber.append(UInt8(quotient)) } } // 把余数对应的字符加入结果 resultChars.append(charSet[remainder]) // 更新数字为商 number = newNumber.isEmpty ? [0] : newNumber } // 反转得到最终字符串 return resultChars.reversed().joined() } } // 使用示例 let bArray: [UInt8] = [169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47, 187, 211] let base36String = bArray.toBase36() print(base36String) // 同样输出预期结果
这个方案完全自定义,不需要任何外部依赖,但需要自己处理大整数的运算逻辑,适合对依赖有严格限制的场景。
总结你的原始思路
你的流程只适用于字节数组长度≤8字节(转UInt64)或≤16字节(转UInt128)的情况,对于SHA1这种20字节的超大数组,必须用任意精度整数来处理,所以上面两种方案才是正确的选择。
内容的提问来源于stack exchange,提问作者Nick Jones
相关产品推荐
相关产品推荐

