Given a hexadecimal string in Swift, convert to hex value

后端 未结 4 715
难免孤独
难免孤独 2021-01-06 18:30

Suppose I am given a string like this:

D7C17A4F

How do I convert each individual character to a hex value?

So D

相关标签:
4条回答
  • 2021-01-06 18:43

    here is the more generic, "pure swift" approach (no Foundation required :-))

    extension UnsignedInteger {
        var hex: String {
            var str = String(self, radix: 16, uppercase: true)
            while str.characters.count < 2 * MemoryLayout<Self>.size {
                str.insert("0", at: str.startIndex)
            }
            return str
        }
    }
    
    extension Array where Element: UnsignedInteger {
        var hex: String {
            var str = ""
            self.forEach { (u) in
                str.append(u.hex)
            }
            return str
        }
    }
    
    let str = [UInt8(1),22,63,41].hex  // "01163F29"
    let str2 = [UInt(1),22,63,41].hex  // "00000000000000010000000000000016000000000000003F0000000000000029"
    
    extension String {
        func toUnsignedInteger<T:UnsignedInteger>()->[T]? {
            var ret = [T]()
            let nibles = MemoryLayout<T>.size * 2
            for i in stride(from: 0, to: characters.count, by: nibles) {
                let start = self.index(startIndex, offsetBy: i)
                guard let end = self.index(start, offsetBy: nibles, limitedBy: endIndex),
                    let ui = UIntMax(self[start..<end], radix: 16) else { return nil }
                ret.append(T(ui))
            }
            return ret
        }
    }
    
    let u0:[UInt8]? = str.toUnsignedInteger()                   // [1, 22, 63, 41]
    let u1 = "F2345f".toUnsignedInteger() as [UInt8]?           // [18, 52, 95]
    let u2 = "12345f".toUnsignedInteger() as [UInt16]?          // nil
    let u3 = "12345g".toUnsignedInteger() as [UInt8]?           // nil
    let u4 = "12345f".toUnsignedInteger() as [UInt]?            // nil
    let u5 = "12345678".toUnsignedInteger() as [UInt8]?         // [18, 52, 86, 120]
    let u6 = "12345678".toUnsignedInteger() as [UInt16]?        // [4660, 22136]
    let u7 = "1234567812345678".toUnsignedInteger() as [UInt]?  // [1311768465173141112]
    

    It is very easily to do the same for SignedInteger as well, but better approach will be to map results to signed type

    let u8 = u1?.map { Int8(bitPattern: $0) }                    // [-14, 52, 95]
    
    0 讨论(0)
  • 2021-01-06 18:45

    A possible solution:

    let string = "D7C17A4F"
    
    let chars = Array(string)
    let numbers = map (stride(from: 0, to: chars.count, by: 2)) {
        strtoul(String(chars[$0 ..< $0+2]), nil, 16)
    }
    

    Using the approach from https://stackoverflow.com/a/29306523/1187415, the string is split into substrings of two characters. Each substring is interpreted as a sequence of digits in base 16, and converted to a number with strtoul().

    Verify the result:

    println(numbers)
    // [215, 193, 122, 79]
    
    println(map(numbers, { String(format: "%02X", $0) } ))
    // [D7, C1, 7A, 4F]
    

    Update for Swift 2 (Xcode 7):

    let string = "D7C17A4F"
    let chars = Array(string.characters)
    
    let numbers = 0.stride(to: chars.count, by: 2).map {
        UInt8(String(chars[$0 ..< $0+2]), radix: 16) ?? 0
    }
    
    print(numbers) 
    

    or

    let string = "D7C17A4F"
    
    var numbers = [UInt8]()
    var from = string.startIndex
    while from != string.endIndex {
        let to = from.advancedBy(2, limit: string.endIndex)
        numbers.append(UInt8(string[from ..< to], radix: 16) ?? 0)
        from = to
    }
    
    print(numbers) 
    

    The second solution looks a bit more complicated but has the small advantage that no additional chars array is needed.

    0 讨论(0)
  • 2021-01-06 19:01

    My variation of @martin-r answer:

    extension String {
    
        func hexToByteArray() -> [UInt8] {
            let byteCount = self.utf8.count / 2
            var array = [UInt8](count: byteCount, repeatedValue: 0)
            var from = self.startIndex
            for i in 0..<byteCount {
                let to = from.successor()
                let sub = self.substringWithRange(from...to)
                array[i] = UInt8(sub, radix: 16) ?? 0
                from = to.successor()
            }
            return array
        }
    
    }
    
    0 讨论(0)
  • 2021-01-06 19:04

    Swift 3 version, modified from @Martin R's answer. This variant also accepts incoming string with odd length.

    let string = "D7C17A4F"
    
    let chars = Array(string.characters)
    let numbers = stride(from: 0, to: chars.count, by: 2).map() {
        strtoul(String(chars[$0 ..< min($0 + 2, chars.count)]), nil, 16)
    }
    
    0 讨论(0)
提交回复
热议问题