我想要Swift中Data值的十六进制表示形式。
最终,我想像这样使用它:
let data = Data(base64Encoded: "aGVsbG8gd29ybGQ=")!
print(data.hexString)
Answers:
一种替代实现(取自“如何使用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()
}
}
我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
以下实现的速度提高了约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)
}
}
此代码Data
使用计算属性扩展类型。它遍历数据字节,并将字节的十六进制表示连接到结果:
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
return (self as NSData).description
return map { String(format: "%02hhx", $0) }.joined()
我的版本。它比Martin R最初接受的答案快10倍。
public extension Data {
private static let hexAlphabet = Array("0123456789abcdef".unicodeScalars)
func hexStringEncoded() -> String {
String(reduce(into: "".unicodeScalars) { result, value in
result.append(Self.hexAlphabet[Int(value / 0x10)])
result.append(Self.hexAlphabet[Int(value % 0x10)])
})
}
}
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)
}
}
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)
}
}
注意:此功能不验证输入。确保仅将其用于具有(偶数个)字符的十六进制字符串。
这实际上并不能回答OP的问题,因为它适用于Swift字节数组,而不是Data对象。它比其他答案要大得多。但是它应该更有效,因为它避免使用String(format:)。
无论如何,希望有人会发现这很有用...
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 the byteArrayToHexString() method
let byteArray : [UInt8] = [ 0x25, 0x99, 0xf3 ]
assert(StringMisc.byteArrayToHexString(byteArray) == "2599F3")
与这里的其他答案有些不同:
extension DataProtocol {
func hexEncodedString(uppercase: Bool = false) -> String {
return self.map {
if $0 < 16 {
return "0" + String($0, radix: 16, uppercase: uppercase)
} else {
return String($0, radix: 16, uppercase: uppercase)
}
}.joined()
}
}
但是,在我的基本XCTest +测量设置中,这是我尝试的4种中最快的。
每次处理1000字节(相同)的随机数据100次:
上图:平均时间:0.028秒,相对标准偏差:1.3%
MartinR:平均时间:0.037秒,相对标准偏差:6.2%
Zyphrax:平均时间:0.032秒,相对标准偏差:2.9%
NickMoore:平均时间:0.039秒,相对标准偏差:2.0%
重复测试返回相同的相对结果。(尼克和马丁斯有时会交换)
extension
苹果课堂上的func
可以使用a的情况,但我喜欢与的对称性base64EncodedString
。