program story

신속한 데이터를 16 진수 문자열로 변환하는 방법

inputbox 2020. 11. 29. 10:25
반응형

신속한 데이터를 16 진수 문자열로 변환하는 방법


Swift에서 데이터 값의 16 진수 표현을 원합니다.

결국 다음과 같이 사용하고 싶습니다.

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

대체 구현 ( How to crypt string to sha1 with Swift?, with an additional option for 대문자 출력)은 다음과 같습니다.

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:).

DataCollection프로토콜을 따르 므로 map()각 바이트를 해당 16 진 문자열에 매핑 하는 사용할 수 있습니다 . %02x형식은 필요한 경우는 앞에 0이 두 자리까지 채워베이스 (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는 계산 된 속성으로 유형을 확장합니다 . 데이터 바이트를 반복하고 바이트의 16 진 표현을 결과에 연결합니다.

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

내 버전. 우아하지는 않지만 Martin R의 대답보다 약 10 배 빠릅니다.

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

Swift 4-데이터에서 16 진수 문자열
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 - From Hex String to Data
I've also added a fast solution for converting a hex String into Data (based on a C solution).

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: this function does not validate the input. Make sure that it is only used for hexadecimal strings with (an even amount of) characters.


This doesn't really answer the OP's question since it works on a Swift byte array, not a Data object. And it's much bigger than the other answers. But it should be more efficient since it avoids using String(format: ).

Anyway, in the hopes someone finds this useful ...

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 case:

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

Take each byte and convert it to hex, then appending it to the accumulated value that starts as an empty string:

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

Maybe not the fastest, but data.map({ String($0, radix: 16) }).joined() does the job. As mentioned in the comments, this solution was flawed.

참고URL : https://stackoverflow.com/questions/39075043/how-to-convert-data-to-hex-string-in-swift

반응형