104 votes

Comment convertir des données en chaîne hexadécimale en Swift ?

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

Finalement, je voudrais l'utiliser comme ça :

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

0 votes

Voir la solution la plus rapide sans copier : stackoverflow.com/a/69477135/8740349

239voto

Martin R Points 105727

Une mise en œuvre simple (tirée de Comment hacher NSString avec SHA1 en Swift ? avec une option supplémentaire pour la sortie en majuscules) serait la suivante

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 self.map { String(format: format, $0) }.joined()
    }
}

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

Data est conforme à la Collection on peut donc utiliser map() pour faire correspondre chaque octet à la chaîne hexagonale correspondante. Le site %02x format imprime l'argument en base 16, rempli jusqu'à deux chiffres avec un zéro en tête si nécessaire. Le site hh fait en sorte que l'argument (qui est passé comme un nombre entier sur la pile) est traité comme un octet d'un octet. On pourrait omettre le modificateur ici car $0 est un non signé nombre ( UInt8 ) et aucune extension de signe ne se produira, mais cela ne fait aucun mal de laisser la laisser.

Le résultat est ensuite joint à une seule chaîne de caractères.

Ejemplo:

let data = Data([0, 1, 127, 128, 255])
// For Swift < 4.2 use:
// 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 d'environ 50 (testée avec 1000 octets aléatoires). Elle s'inspire de La solution de RenniePet y La solution de Nick Moore mais tire parti de String(unsafeUninitializedCapacity:initializingUTF8With:) qui a été introduit avec Swift 5.3/Xcode 12 et est disponible sur macOS 11 et iOS 14 ou plus récent.

Cette méthode permet de créer une chaîne Swift à partir d'unités UTF-8 de manière efficace, sans copie ou réallocation inutile.

Une implémentation alternative pour les anciennes versions de macOS/iOS est également fournie.

extension Data {
    struct HexEncodingOptions: OptionSet {
        let rawValue: Int
        static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
    }

    func hexEncodedString(options: HexEncodingOptions = []) -> String {
        let hexDigits = options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef"
        if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
            let utf8Digits = Array(hexDigits.utf8)
            return String(unsafeUninitializedCapacity: 2 * self.count) { (ptr) -> Int in
                var p = ptr.baseAddress!
                for byte in self {
                    p[0] = utf8Digits[Int(byte / 16)]
                    p[1] = utf8Digits[Int(byte % 16)]
                    p += 2
                }
                return 2 * self.count
            }
        } else {
            let utf16Digits = Array(hexDigits.utf16)
            var chars: [unichar] = []
            chars.reserveCapacity(2 * self.count)
            for byte in self {
                chars.append(utf16Digits[Int(byte / 16)])
                chars.append(utf16Digits[Int(byte % 16)])
            }
            return String(utf16CodeUnits: chars, count: chars.count)
        }
    }
}

1 votes

Bien que je n'aime généralement pas une extension sur une classe Apple quand une func peut être utilisé J'aime la symétrie avec base64EncodedString .

2 votes

@reza_khalafi : Il existe de nombreuses solutions pour Objective-C, par exemple ici : stackoverflow.com/questions/1305225/ .

2 votes

Pouvez-vous également fournir une opération inverse de la chaîne de caractères vers les données hexagonales ?

39voto

marius Points 852

Ce code étend le Data avec une propriété calculée. Il itère à travers 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)}
    }
}

1 votes

Bien que ce code puisse aider à résoudre le problème, il ne permet pas d'expliquer pourquoi et/ou comment il répond à la question. Fournir ce contexte supplémentaire améliorerait considérablement sa valeur à long terme. S'il vous plaît modifier votre réponse pour ajouter une explication, y compris les limitations et les hypothèses applicables.

7 votes

Vous pouvez simplement le convertir en NSData et obtenir sa description. return (self as NSData).description

7 votes

Mon préféré (de stackoverflow.com/a/25762128/1187415 ) : return map { String(format: "%02hhx", $0) }.joined()

31voto

Nick Moore Points 7897

Ma version. Elle est environ 10 fois plus rapide que la réponse acceptée [originale] par Martin R.

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

1 votes

C'est élégant, Nick. Merci !

1 votes

J'ai pu l'améliorer un peu si vous voulez intégrer mes modifications dans cette réponse : gist.github.com/BenLeggiero/916bf788000736a7c0e6d1cad6e54410

15voto

Zyphrax Points 6603

Swift 4 - Des données à la chaîne hexagonale
Sur la base de La solution de Martin R mais même un tout petit peu plus rapide.

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 hexagonale aux données
J'ai également ajouté une solution rapide pour convertir une chaîne hexagonale en données (basée sur un 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. Veillez à ce qu'elle ne soit utilisée que pour les chaînes hexadécimales comportant (un nombre pair) de caractères.

5voto

RenniePet Points 2388

Cela ne répond pas vraiment à la question de l'OP puisque cela fonctionne sur un tableau d'octets Swift, et non sur un objet Data. Et elle est beaucoup plus grosse que les autres réponses. Mais elle devrait être plus efficace puisqu'elle évite d'utiliser String(format : ).

Quoi qu'il en soit, j'espère que quelqu'un trouvera 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")

0 votes

Voici une version plus Swifty de cette réponse : gist.github.com/BenLeggiero/ce1e62fe0194ca969eb7cdda6639a011

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X