【问题标题】:Given a hexadecimal string in Swift, convert to hex value给定 Swift 中的十六进制字符串,转换为十六进制值
【发布时间】:2015-07-23 17:41:25
【问题描述】:

假设给我一个这样的字符串:

D7C17A4F

如何将每个单独的字符转换为十六进制值?

所以D 应该是0xD7 应该是0x7...

现在,我将每个单独的字符表示为 ASCII 值。 D68755。我正在尝试将这两个值打包成一个字节。例如:D7 变为 0xD7C1 变为 0xC1。不过,我不能使用 ASCII 十进制值来做到这一点。

【问题讨论】:

  • 您想要一个包含 8 个数字(13、7、...)的数组还是包含 4 个数字(0xD7、0xC1、...)的数组?字符串总是 8 个十六进制字符长,还是可以更长或更短?
  • 字符串长度始终为 32 个字符。 4个数字的数组更好。任何一个都可以
  • 但是一串32个字符应该是16个数字吧?

标签: swift hex ascii


【解决方案1】:

使用chunks

"D7C17A4F"
  .chunks(ofCount: 2)
  .map { UInt8($0, radix: 0x10)! }

【讨论】:

    【解决方案2】:

    这里是更通用的“纯快速”方法(不需要 Foundation :-))

    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]
    

    对 SignedInteger 也很容易做同样的事情,但更好的方法是将结果映射到有符号类型

    let u8 = u1?.map { Int8(bitPattern: $0) }                    // [-14, 52, 95]
    

    【讨论】:

      【解决方案3】:

      Swift 3 版本,根据@Martin R 的回答修改。此变体还接受奇数长度的传入字符串。

      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)
      }
      

      【讨论】:

        【解决方案4】:

        我对@martin-r 答案的变体:

        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
            }
        
        }
        

        【讨论】:

          【解决方案5】:

          一个可能的解决方案:

          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)
          }
          

          使用https://stackoverflow.com/a/29306523/1187415的方法, 字符串被分成两个字符的子字符串。 每个子字符串都被解释为一个数字序列 以 16 为基数,并转换为带有 strtoul() 的数字。

          验证结果:

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

          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) 
          

          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) 
          

          第二种解决方案看起来有点复杂,但体积很小 优点是不需要额外的chars 数组。

          【讨论】:

          • 在 swift 2 中大步使用地图发生了什么?这似乎是一个优雅的解决方案。
          猜你喜欢
          • 2018-01-22
          • 2019-07-27
          • 2018-01-31
          • 2012-02-03
          • 1970-01-01
          • 2017-08-12
          • 1970-01-01
          • 2020-03-14
          • 2020-06-20
          相关资源
          最近更新 更多