nshtmltextdocumenttype - Cómo convertir datos a una cadena hexadecimal en Swift
html in label swift (7)
Quiero la representación hexadecimal de un valor de datos en Swift.
Eventualmente me gustaría usarlo así:
let data = Data(base64Encoded: "aGVsbG8gd29ybGQ=")!
print(data.hexString)
Este código extiende el tipo de
Data
con una propiedad calculada.
Se itera a través de los bytes de datos y concatena la representación hexadecimal del byte con el resultado:
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
Esto realmente no responde a la pregunta del OP ya que funciona en una matriz de bytes Swift, no en un objeto de datos. Y es mucho más grande que las otras respuestas. Pero debería ser más eficiente ya que evita usar String (format:).
De todos modos, con la esperanza de que alguien encuentre esto útil ...
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
}
}
Caso de prueba:
// Test the byteArrayToHexString() method
let byteArray : [UInt8] = [ 0x25, 0x99, 0xf3 ]
assert(StringMisc.byteArrayToHexString(byteArray) == "2599F3")
Mi version. No es tan elegante, pero es aproximadamente 10 veces más rápido que la respuesta aceptada por 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)])
}))
}
}
Sería una implementación alternativa (tomada de Cómo encriptar cadena a sha1 con Swift?, Con una opción adicional para salida en mayúscula)
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()
}
}
Elegí un
hexEncodedString(options:)
en el estilo del método existente
base64EncodedString(options:)
.
Data
ajustan al protocolo de recopilación, por lo tanto, se puede usar
map()
para asignar cada byte a la cadena hexadecimal correspondiente.
El formato
%02x
imprime el argumento en base 16, rellenado hasta dos dígitos con un cero a la
%02x
si es necesario.
El modificador
hh
hace que el argumento (que se pasa como un entero en la pila) se trate como una cantidad de un byte.
Uno podría omitir el modificador aquí porque
$0
es un número
sin signo
(
UInt8
) y no se producirá una extensión de signo, pero no hace daño dejarlo.
El resultado se une a una sola cadena.
Ejemplo:
let data = Data(bytes: [0, 1, 127, 128, 255])
print(data.hexEncodedString()) // 00017f80ff
print(data.hexEncodedString(options: .upperCase)) // 00017F80FF
La siguiente implementación es más rápida en un factor de aproximadamente 120 (probado con 1000 bytes aleatorios). Es similar a la solución de RenniePet y la solución de Nick Moore , pero se basa en unidades de código UTF-16, que es lo que las cadenas Swift (actualmente) usan como almacenamiento interno.
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)
}
}
Tome cada byte y conviértalo a hexadecimal, luego añádalo al valor acumulado que comienza como una cadena vacía:
extension Data {
var hexString: String {
return self.reduce("", { $0 + String(format: "%02x", $1) })
}
}
Quizás no sea el más rápido, pero
Como se mencionó en los comentarios, esta solución fue defectuosa.
data.map({ String($0, radix: 16) }).joined()
hace el trabajo.
Swift 4: de datos a cadena hexadecimal
Basado en
la solución de Martin R,
pero incluso un poco más rápido.
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: de cadena hexadecimal a datos
También he agregado una solución rápida para convertir una cadena hexadecimal en datos (basada en una
solución 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)
}
}
Nota: esta función no valida la entrada. Asegúrese de que solo se use para cadenas hexadecimales con (una cantidad par) de caracteres.