web-dev-qa-db-fra.com

Comment convertir des données en chaîne hexadécimale dans swift

Je veux la représentation hexadécimale d'une valeur de données dans Swift.

Finalement, je voudrais l'utiliser comme ceci:

let data = Data(base64Encoded: "aGVsbG8Gd29ybGQ=")!
print(data.hexString)
51
marius

Une autre implémentation (tirée de Comment chiffrer une chaîne dans sha1 avec Swift? , avec une option supplémentaire pour une sortie en majuscule) serait:

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

J'ai choisi une méthode hexEncodedString(options:) dans le style de la méthode existante base64EncodedString(options:).

Data est conforme au protocole Collection, vous pouvez donc utiliser map() pour mapper chaque octet sur la chaîne hexagonale correspondante . Le format %02x imprime l'argument en base 16, avec deux chiffres au maximum. si nécessaire. Le modificateur hh fait en sorte que l'argument .__ (qui est transmis sous forme d'entier sur la pile) soit traité comme une quantité d'un octet Ici, vous pouvez omettre le modificateur car $0 est un unsigned Nombre (UInt8) et aucune extension de signe ne se produira, mais il ne fait aucun mal en laissantit dans.

Le résultat est ensuite joint à une seule chaîne.

Exemple:

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

L’implémentation suivante est plus rapide d’un facteur 120 environ (testée avec 1000 octets aléatoires). Elle est similaire à la solution de RenniePet Et à la solution de Nick Moore , mais basée sur les unités de code UTF-16, qui est ce que les chaînes de caractères 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)
    }
}
140
Martin R

Ma version. Ce n'est pas aussi élégant, mais c'est environ 10 fois plus rapide que la réponse acceptée de 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)])
        }))
    }
}
14
Nick Moore

Ce code étend le type Data avec une propriété calculée. Il parcourt les octets de données et concatène la représentation hexadécimale de l'octet au résultat:

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

Cela ne répond pas vraiment à la question du PO car cela fonctionne sur un tableau d'octets Swift, pas sur un objet Data. Et c'est beaucoup plus gros que les autres réponses. Mais cela devrait être plus efficace car cela évite d’utiliser String (format:). 

Quoi qu'il en soit, dans l'espoir que quelqu'un trouve cela utile ...

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

Cas de test:

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

Swift 4 - De la donnée à la chaîne hexadécimale
Basé sur la solution de Martin R mais même un tout petit peu plus vite. 

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 la chaîne hexadécimale aux données
J'ai également ajouté une solution rapide pour convertir une chaîne hexadécimale en données (basée sur une solution 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)
  }
}

Note: cette fonction ne valide pas l'entrée. Assurez-vous qu'il n'est utilisé que pour les chaînes hexadécimales avec (un nombre pair) de caractères.

4
Zyphrax

Prenez chaque octet et convertissez-le en hexadécimal, puis ajoutez-le à la valeur accumulée qui commence par une chaîne vide:

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

Peut-être pas le plus rapide, mais data.map({ String($0, radix: 16) }).joined() fait le travail. Comme mentionné dans les commentaires, cette solution était défectueuse.

0
DeFrenZ