Swift中的十六进制/二进制字符串转换

3

Python有两个非常实用的库方法(binascii.a2b_hex(keyStr)和binascii.hexlify(keyBytes)),我一直在Swift中苦苦挣扎。 在Swift中是否有任何现成的解决方案? 如果没有,怎样实现它?假设所有边界和其他检查(如偶数长度密钥)都已完成。


@匿名用户 这些答案不适用于大数。我指的是32位十六进制数字。高效的方法是拥有这样一个大字符串,并将输入转化为字节数组。目前为止,Swift标准库中没有BigInteger和这种类型的转换。 - johndoe
3个回答

21

Swift 3中的数据没有“内置”的方法将其内容打印为十六进制字符串,也没有创建Data值的方法从十六进制字符串中创建一个Data值。

可以在如何在Swift中将Data转换为十六进制字符串如何使用Swift打印类型为Data的变量的内容?在Swift 3.0中将String转换为Data等地方找到“Data到十六进制字符串”方法。这里是第一个链接中的实现:

extension Data {
    func hexEncodedString() -> String {
        return map { String(format: "%02hhx", $0) }.joined()
    }
}

这是一个可能的反转“十六进制字符串到数据”转换的实现(取自Code Review上的Hex String to Bytes (NSData),翻译为Swift 3并进行了改进),作为一个可失败的初始化器:
extension Data {

    init?(fromHexEncodedString string: String) {

        // Convert 0 ... 9, a ... f, A ...F to their decimal value,
        // return nil for all other input characters
        func decodeNibble(u: UInt8) -> UInt8? {
            switch(u) {
            case 0x30 ... 0x39:
                return u - 0x30
            case 0x41 ... 0x46:
                return u - 0x41 + 10
            case 0x61 ... 0x66:
                return u - 0x61 + 10
            default:
                return nil
            }
        }

        self.init(capacity: string.utf8.count/2)
        
        var iter = string.utf8.makeIterator()
        while let c1 = iter.next() {
            guard
                let val1 = decodeNibble(u: c1),
                let c2 = iter.next(),
                let val2 = decodeNibble(u: c2)
            else { return nil }
            self.append(val1 << 4 + val2)
        }
    }
}

例子:

// Hex string to Data:
if let data = Data(fromHexEncodedString: "0002468A13579BFF") {
    let idata = Data(data.map { 255 - $0 })
    
    // Data to hex string:
    print(idata.hexEncodedString()) // fffdb975eca86400
} else {
    print("invalid hex string")
}

2
这值得100万分。非常干净和有用。感谢分享! - dustinrwh
Swift中的Bool类型有一个名为toggle的可变方法,可以使用even.toggle()调用。 - Leo Dabus
1
@LeoDabus:是的,谢谢!(这是在我写这个答案之后引入的Swift 4.2。)-无论如何,我决定摆脱切换。现在使用UTF-8表示更自然,因为这是自Swift 5以来的本地表示。 - Martin R
@MartinR,希望你不介意我编辑了你的帖子。你应该附加val1 << 4 + val2而不是val1 << 8 + val2。顺便问一下,为什么不像我一样将初始化器设置为通用的呢?这将允许您初始化Data以及字节数组。使用Data capacity初始化器是否比在初始化emptyCollection后调用reserveCapacity有任何优势? - Leo Dabus
1
@LeoDabus:也许可以使用string.withCString(...)。也许我有时会尝试一下。但我喜欢当前的实现,因为它相对较短,易于理解,并且在我的(少量)测试中速度相当快。 - Martin R
显示剩余2条评论

1

我不是很熟悉Python以及它在转换数字时所执行的检查,但您可以扩展以下函数:

func convert(_ str: String, fromRadix r1: Int, toRadix r2: Int) -> String? {
    if let num = Int(str, radix: r1) {
        return String(num, radix: r2)
    } else {
        return nil
    }
}

convert("11111111", fromRadix: 2, toRadix: 16)
convert("ff", fromRadix: 16, toRadix: 2)

0

Swift 2

extension NSData {
    class func dataFromHexString(hex: String) -> NSData? {
        let regex = try! NSRegularExpression(pattern: "^[0-9a-zA-Z]*$", options: .CaseInsensitive)
        let validate = regex.firstMatchInString(hex, options: NSMatchingOptions.init(rawValue: 0), range: NSRange(location: 0, length: hex.characters.count))
        if validate == nil || hex.characters.count % 2 != 0 {
            return nil
        }
        let data = NSMutableData()
        for i in 0..<hex.characters.count/2 {
            let hexStr = hex.substring(i * 2, length: 2)
            var ch: UInt32 = 0
            NSScanner(string: hexStr).scanHexInt(&ch)
            data.appendBytes(&ch, length: 1)
        }
        return data
    }
}

let a = 0xabcd1234
print(String(format: "%x", a)) // Hex to String
NSData.dataFromHexString("abcd1234") // String to hex

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