Ich möchte die hexadezimale Darstellung eines Datenwerts in Swift.
Irgendwann möchte ich es so verwenden:
let data = Data(base64Encoded: "aGVsbG8Gd29ybGQ=")!
print(data.hexString)
Eine alternative Implementierung (übernommen aus Wie man den String mit shift zu sha1? verschlüsselt, mit einer zusätzlichen Option für die Ausgabe in Großbuchstaben) wäre
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()
}
}
Ich wählte eine hexEncodedString(options:)
-Methode im Stil der vorhandenen Methode base64EncodedString(options:)
.
Data
entspricht dem Collection
-Protokoll. Daher kann mit __.map()
jedes Byte der entsprechenden Hex-Zeichenfolge ..__ zugeordnet werden. Das %02x
-Format druckt das Argument in der Basis 16, gefüllt mit zwei Ziffern mit einer führenden Null wenn erforderlich. Der hh
-Modifizierer bewirkt, dass das Argument (Das als Ganzzahl im Stapel übergeben wird) als ein Byte Menge behandelt wird. Man könnte den Modifikator hier weglassen, weil $0
eine unsigned Nummer (UInt8
) ist und keine Vorzeichenerweiterung auftritt, aber es schadet nicht.
Das Ergebnis wird dann zu einer einzelnen Zeichenfolge verbunden.
Beispiel:
let data = Data(bytes: [0, 1, 127, 128, 255])
print(data.hexEncodedString()) // 00017f80ff
print(data.hexEncodedString(options: .upperCase)) // 00017F80FF
Die folgende Implementierung ist um den Faktor 120 schneller (getestet mit 1000 zufälligen Bytes). Es ist ähnlich wie RenniePets Lösung Und Nick Moore's Lösung , basiert aber auf UTF-16-Code-Units, die Swift -Strings (derzeit) als internen Speicher verwenden.
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)
}
}
Meine Version. Es ist nicht so elegant, aber es ist etwa zehnmal schneller als die akzeptierte Antwort von Martin R.
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)])
}))
}
}
Dieser Code erweitert den Typ Data
um eine berechnete Eigenschaft. Es iteriert durch die Bytes der Daten und verkettet die hexadezimale Darstellung des Bytes mit dem Ergebnis:
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
Dies beantwortet die Frage des OP nicht wirklich, da es auf einem Swift-Byte-Array und nicht auf einem Data-Objekt arbeitet. Und es ist viel größer als die anderen Antworten. Es sollte jedoch effizienter sein, da es die Verwendung von String (format:) vermeidet.
Wie auch immer, in der Hoffnung, jemand findet das nützlich ...
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
}
}
Testfall:
// Test the byteArrayToHexString() method
let byteArray : [UInt8] = [ 0x25, 0x99, 0xf3 ]
assert(StringMisc.byteArrayToHexString(byteArray) == "2599F3")
Swift 4 - Von Daten zu Hex-String
Basierend auf Martin Rs Lösung aber noch ein bisschen schneller.
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 - Vom Hex-String zu Daten
Ich habe auch eine schnelle Lösung hinzugefügt, um einen Hex-String in Daten umzuwandeln (basierend auf einer C-Lösung ).
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)
}
}
Hinweis: Diese Funktion validiert die Eingabe nicht. Stellen Sie sicher, dass es nur für hexadezimale Zeichenfolgen mit (einer geraden Anzahl von) Zeichen verwendet wird.
Nehmen Sie jedes Byte, konvertieren Sie es in Hex und hängen Sie es an den akkumulierten Wert an, der als leere Zeichenfolge beginnt:
extension Data {
var hexString: String {
return self.reduce("", { $0 + String(format: "%02x", $1) })
}
}
Vielleicht nicht der Schnellste, aber Wie in den Kommentaren erwähnt, war diese Lösung fehlerhaft.data.map({ String($0, radix: 16) }).joined()
erledigt die Arbeit.