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

时间:2023-01-05 18:51:51

I want the hexadecimal representation of a Data value in Swift.

我想要一个数据值的十六进制表示形式为Swift。

Eventually I'd want to use it like this:

最后我想这样使用它:

let data = Data(base64Encoded: "aGVsbG8gd29ybGQ=")!
print(data.hexString)

4 个解决方案

#1


114  

An alternative implementation (taken from How to crypt string to sha1 with Swift?, with an additional option for uppercase output) would be

另一种实现(从如何使用Swift将crypt字符串转换为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()
    }
}

I chose a hexEncodedString(options:) method in the style of the existing method base64EncodedString(options:).

我以现有方法base64EncodedString(选项:)的样式选择了一个hexEncodedString(选项:)方法。

Data conforms to the Collection protocol, therefore one can use map() to map each byte to the corresponding hex string. The %02x format prints the argument in base 16, filled up to two digits with a leading zero if necessary. The hh modifier causes the argument (which is passed as an integer on the stack) to be treated as a one byte quantity. One could omit the modifier here because $0 is an unsigned number (UInt8) and no sign-extension will occur, but it does no harm leaving it in.

数据符合收集协议,因此可以使用map()将每个字节映射到相应的十六进制字符串。%02x格式打印以16为基数的参数,如果有必要,最多填充两位数的前导0。hh修饰符将使参数(在堆栈上作为整数传递)被视为一个字节量。在这里可以省略修饰符,因为$0是一个未签名的数字(UInt8),并且不会发生符号扩展,但是它不会造成伤害。

The result is then joined to a single string.

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

Example:

例子:

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

The following implementation is faster by a factor about 120 (tested with 1000 random bytes). It is similar to RenniePet's solution and Nick Moore's solution, but based on UTF-16 code units, which is what Swift strings (currently) use as internal storage.

下面的实现速度快了大约120倍(使用1000个随机字节进行测试)。它类似于RenniePet的解决方案和Nick Moore的解决方案,但是基于UTF-16代码单元,这是Swift string(当前)使用的内部存储。

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

#2


6  

My version. It's not as elegant, but it's about 10 times faster than the accepted answer by Martin R.

我的版本。它不那么优雅,但比马丁·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)])
        }))
    }
}

#3


5  

This doesn't really answer the OP's question since it works on a Swift byte array, not a Data object. And it's much bigger than the other answers. But it should be more efficient since it avoids using String(format: ).

这并不能真正回答OP的问题,因为它在一个Swift字节数组上工作,而不是数据对象。它比其他答案要大得多。但是它应该更有效,因为它避免使用String(format:)。

Anyway, in the hopes someone finds this useful ...

不管怎么说,希望有人能发现这个有用的东西……

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

Test case:

测试用例:

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

#4


5  

This code extends the Data type with a computed property. It iterates through the bytes of data and concatenates the byte's hex representation to the result:

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

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

#1


114  

An alternative implementation (taken from How to crypt string to sha1 with Swift?, with an additional option for uppercase output) would be

另一种实现(从如何使用Swift将crypt字符串转换为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()
    }
}

I chose a hexEncodedString(options:) method in the style of the existing method base64EncodedString(options:).

我以现有方法base64EncodedString(选项:)的样式选择了一个hexEncodedString(选项:)方法。

Data conforms to the Collection protocol, therefore one can use map() to map each byte to the corresponding hex string. The %02x format prints the argument in base 16, filled up to two digits with a leading zero if necessary. The hh modifier causes the argument (which is passed as an integer on the stack) to be treated as a one byte quantity. One could omit the modifier here because $0 is an unsigned number (UInt8) and no sign-extension will occur, but it does no harm leaving it in.

数据符合收集协议,因此可以使用map()将每个字节映射到相应的十六进制字符串。%02x格式打印以16为基数的参数,如果有必要,最多填充两位数的前导0。hh修饰符将使参数(在堆栈上作为整数传递)被视为一个字节量。在这里可以省略修饰符,因为$0是一个未签名的数字(UInt8),并且不会发生符号扩展,但是它不会造成伤害。

The result is then joined to a single string.

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

Example:

例子:

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

The following implementation is faster by a factor about 120 (tested with 1000 random bytes). It is similar to RenniePet's solution and Nick Moore's solution, but based on UTF-16 code units, which is what Swift strings (currently) use as internal storage.

下面的实现速度快了大约120倍(使用1000个随机字节进行测试)。它类似于RenniePet的解决方案和Nick Moore的解决方案,但是基于UTF-16代码单元,这是Swift string(当前)使用的内部存储。

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

#2


6  

My version. It's not as elegant, but it's about 10 times faster than the accepted answer by Martin R.

我的版本。它不那么优雅,但比马丁·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)])
        }))
    }
}

#3


5  

This doesn't really answer the OP's question since it works on a Swift byte array, not a Data object. And it's much bigger than the other answers. But it should be more efficient since it avoids using String(format: ).

这并不能真正回答OP的问题,因为它在一个Swift字节数组上工作,而不是数据对象。它比其他答案要大得多。但是它应该更有效,因为它避免使用String(format:)。

Anyway, in the hopes someone finds this useful ...

不管怎么说,希望有人能发现这个有用的东西……

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

Test case:

测试用例:

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

#4


5  

This code extends the Data type with a computed property. It iterates through the bytes of data and concatenates the byte's hex representation to the result:

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

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