如何在swift中将数据转换为十六进制字符串

mar*_*ius 62 swift swift3

我想要在Swift中使用数据值的十六进制表示.

最终我想要像这样使用它:

let data = Data(base64Encoded: "aGVsbG8gd29ybGQ=")!
print(data.hexString)
Run Code Online (Sandbox Code Playgroud)

Mar*_*n R 161

另一种实现方式(取自如何使用Swift来加密字符串到sha1?,还有一个大写输出的附加选项)

extension Data {
    struct HexEncodingOptions: OptionSet {
        let rawValue: Int
        static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
    }

    func hexEncodedString(options: HexEncodingOptions = []) -> String {
        let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
        return map { String(format: format, $0) }.joined()
    }
}
Run Code Online (Sandbox Code Playgroud)

我选择了hexEncodedString(options:)一种现有方法风格的方法base64EncodedString(options:).

Data符合Collection协议,因此可以使用 map()将每个字节映射到相应的十六进制字符串.的%02x格式打印在基座16中的参数,填充至两位数字与如果需要一个前导零.的hh改性剂引起的参数(其为在栈上的整数传递)被当作一个字节的数量.这里可以省略修饰符,因为它$0是一个无符号 数字(UInt8),并且不会出现符号扩展,但它不会有任何损害.

然后将结果连接到单个字符串.

例:

let data = Data(bytes: [0, 1, 127, 128, 255])
print(data.hexEncodedString()) // 00017f80ff
print(data.hexEncodedString(options: .upperCase)) // 00017F80FF
Run Code Online (Sandbox Code Playgroud)

以下实现更快约120(用1000个随机字节测试).它类似于 RenniePet的解决方案Nick Moore的解决方案,但基于UTF-16代码单元,这是Swift字符串(当前)用作内部存储的.

extension Data {
    struct HexEncodingOptions: OptionSet {
        let rawValue: Int
        static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
    }

    func hexEncodedString(options: HexEncodingOptions = []) -> String {
        let hexDigits = Array((options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef").utf16)
        var chars: [unichar] = []
        chars.reserveCapacity(2 * count)
        for byte in self {
            chars.append(hexDigits[Int(byte / 16)])
            chars.append(hexDigits[Int(byte % 16)])
        }
        return String(utf16CodeUnits: chars, count: chars.count)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @reza_khalafi:Objective-C 有很多解决方案,例如这里:http://stackoverflow.com/questions/1305225/best-way-to-serialize-an-nsdata-into-a-hexadeximal-string。 (2认同)
  • 您还可以提供从字符串到十六进制数据的反向操作吗 (2认同)

mar*_*ius 18

此代码Data使用computed属性扩展类型.它遍历数据字节并将字节的十六进制表示连接到结果:

extension Data {
    var hexDescription: String {
        return reduce("") {$0 + String(format: "%02x", $1)}
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您可以将它转换为NSData并获取其描述`return(self as NSData).description` (6认同)
  • 我最喜欢的(来自http://stackoverflow.com/a/25762128/1187415):`return map {String(格式:"%02hhx",$ 0)}.join()` (4认同)

Nic*_*ore 17

我的版本.它并不优雅,但它比Martin R.接受的答案快10倍.

extension Data {
    private static let hexAlphabet = "0123456789abcdef".unicodeScalars.map { $0 }

    public func hexEncodedString() -> String {
        return String(self.reduce(into: "".unicodeScalars, { (result, value) in
            result.append(Data.hexAlphabet[Int(value/16)])
            result.append(Data.hexAlphabet[Int(value%16)])
        }))
    }
}
Run Code Online (Sandbox Code Playgroud)


Zyp*_*rax 8

Swift 4-从数据到十六进制字符串
基于Martin R的解决方案,但速度甚至更快。

extension Data {
  /// A hexadecimal string representation of the bytes.
  func hexEncodedString() -> String {
    let hexDigits = Array("0123456789abcdef".utf16)
    var hexChars = [UTF16.CodeUnit]()
    hexChars.reserveCapacity(count * 2)

    for byte in self {
      let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
      hexChars.append(hexDigits[index1])
      hexChars.append(hexDigits[index2])
    }

    return String(utf16CodeUnits: hexChars, count: hexChars.count)
  }
}
Run Code Online (Sandbox Code Playgroud)

Swift 4-从十六进制字符串到数据
我还添加了一个快速解决方案,用于将十六进制字符串转换为Data(基于C解决方案)。

extension String {
  /// A data representation of the hexadecimal bytes in this string.
  func hexDecodedData() -> Data {
    // Get the UTF8 characters of this string
    let chars = Array(utf8)

    // Keep the bytes in an UInt8 array and later convert it to Data
    var bytes = [UInt8]()
    bytes.reserveCapacity(count / 2)

    // It is a lot faster to use a lookup map instead of strtoul
    let map: [UInt8] = [
      0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
      0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
      0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // HIJKLMNO
    ]

    // Grab two characters at a time, map them and turn it into a byte
    for i in stride(from: 0, to: count, by: 2) {
      let index1 = Int(chars[i] & 0x1F ^ 0x10)
      let index2 = Int(chars[i + 1] & 0x1F ^ 0x10)
      bytes.append(map[index1] << 4 | map[index2])
    }

    return Data(bytes)
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:此功能不验证输入。确保仅将其用于具有(偶数个)字符的十六进制字符串。


Top*_*ter 7

向后兼容且快速的解决方案:

extension Data {
    /// Fast convert to hex by reserving memory (instead of mapping and join).
    public func toHex(uppercase: Bool = false) -> String {
        // Constants (Hex has 2 characters for each Byte).
        let size = self.count * 2;
        let degitToCharMap = Array((
            uppercase ? "0123456789ABCDEF" : "0123456789abcdef"
        ).utf16);
        // Reserve dynamic memory (plus one for null termination).
        let buffer = UnsafeMutablePointer<unichar>.allocate(capacity: size + 1);
        // Convert each byte.
        var index = 0
        for byte in self {
            buffer[index] = degitToCharMap[Int(byte / 16)];
            index += 1;
            buffer[index] = degitToCharMap[Int(byte % 16)];
            index += 1;
        }
        // Set Null termination.
        buffer[index] = 0;
        // Casts to string (without any copying).
        return String(utf16CodeUnitsNoCopy: buffer,
                      count: size, freeWhenDone: true)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,上面将所有权传递buffer给了返回的String对象。

还要知道,因为 Swift 的内部String数据是UTF16(但可以UTF8从 Swift 5 开始),接受的答案中提供的所有解决方案都会进行完整复制(并且速度较慢),至少如果不是的话#available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *);-)

正如我的个人资料中提到的,Apache 2.0也允许在许可下使用(无需归属)。


Ren*_*Pet 5

这并没有真正回答OP的问题,因为它适用于Swift字节数组,而不是Data对象.它比其他答案要大得多.但它应该更有效,因为它避免使用String(格式:).

无论如何,希望有人觉得这很有用......

public class StringMisc {

   // MARK: - Constants

   // This is used by the byteArrayToHexString() method
   private static let CHexLookup : [Character] =
      [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]


   // Mark: - Public methods

   /// Method to convert a byte array into a string containing hex characters, without any
   /// additional formatting.
   public static func byteArrayToHexString(_ byteArray : [UInt8]) -> String {

      var stringToReturn = ""

      for oneByte in byteArray {
         let asInt = Int(oneByte)
         stringToReturn.append(StringMisc.CHexLookup[asInt >> 4])
         stringToReturn.append(StringMisc.CHexLookup[asInt & 0x0f])
      }
      return stringToReturn
   }
}
Run Code Online (Sandbox Code Playgroud)

测试用例:

  // Test the byteArrayToHexString() method
  let byteArray : [UInt8] = [ 0x25, 0x99, 0xf3 ]
  assert(StringMisc.byteArrayToHexString(byteArray) == "2599F3")
Run Code Online (Sandbox Code Playgroud)