데이터를 16진수 문자열로 빠르게 변환하는 방법
저는 스위프트에서 데이터 값을 16진수로 표현하고 싶습니다.
결국 이렇게 사용하고 싶습니다.
let data = Data(base64Encoded: "aGVsbG8gd29ybGQ=")!
print(data.hexString)
간단한 구현(Swift?에서 SHA1로 NSString을 해시하는 방법에서 가져온 것으로 대문자 출력에 대한 추가 옵션이 있습니다)은 다음과 같습니다.
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()
}
}
저는 선택했습니다.hexEncodedString(options:)
기존 방법 스타일의 방법base64EncodedString(options:)
.
Data
에 적합한Collection
프로토콜, 따라서 사용할 수 있습니다.map()
각 바이트를 대응하는 16진수 문자열에 매핑합니다.%02x
format은 필요한 경우 선두 0으로 두 자리까지 채워지는 베이스 16에서 인수를 출력합니다.hh
modifier를 사용하면 인수(스택에서 정수로 전달됨)가 1바이트 수량으로 처리됩니다.여기에 수식어를 생략할 수 있는 이유는$0
는 부호가 없는 숫자(UInt8
) 및 사인 표시는 발생하지 않지만, 이를 그대로 두는 것은 해가 되지 않습니다.
그런 다음 결과는 단일 문자열에 결합됩니다.
예:
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
다음 구현은 약 50배 빠른 속도입니다(1000개의 랜덤 바이트로 테스트됨).RenniePet의 솔루션과 Nick Moore의 솔루션에서 영감을 받았지만 Swift 5.3/Xcode 12와 함께 도입되었으며 macOS 11 및 iOS 14 이상에서 사용할 수 있습니다.
이 방법을 사용하면 불필요한 복사나 재할당 없이 효율적으로 UTF-8 유닛에서 Swift 문자열을 만들 수 있습니다.
이전 macOS/iOS 버전에 대한 대체 구현도 제공됩니다.
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)
}
}
}
이 코드는 다음을 확장합니다.Data
계산된 속성을 가진 타자데이터의 바이트를 반복하고 바이트의 16진수 표현을 결과와 연결합니다.
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
제 버전.마틴 R의 [원래] 대답보다 10배 정도 빠릅니다.
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)])
})
}
}
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 - 16진수 문자열에서 데이터로
또한 16진수 문자열을 데이터(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)
}
}
참고: 이 함수는 입력의 유효성을 검사하지 않습니다.문자가 짝수인 16진수 문자열에만 사용되는지 확인합니다.
역호환성 및 빠른 솔루션:
extension Data {
/// Fast convert to hex by reserving memory (instead of mapping and join).
public func toHex(uppercase: Bool = false) -> String {
// Constants (Hex has 2 characters for each Byte).
let size = self.count * 2;
let degitToCharMap = Array((
uppercase ? "0123456789ABCDEF" : "0123456789abcdef"
).utf16);
// Reserve dynamic memory (plus one for null termination).
let buffer = UnsafeMutablePointer<unichar>.allocate(capacity: size + 1);
// Convert each byte.
var index = 0
for byte in self {
buffer[index] = degitToCharMap[Int(byte / 16)];
index += 1;
buffer[index] = degitToCharMap[Int(byte % 16)];
index += 1;
}
// Set Null termination.
buffer[index] = 0;
// Casts to string (without any copying).
return String(utf16CodeUnitsNoCopy: buffer,
count: size, freeWhenDone: true)
}
}
위의 내용은 다음과 같습니다.
buffer
다에게String
물건.그리고 스위프트의 내부가
String
는UTF16
(그러나 될 수 있음)UTF8
5), 은 전체 느림)를합니다. Swift 5)후,된서든는은체및사다를도가지림을우및를e(지도을다우(ltdo,5가l사,rnsrdt림#available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
;-)내 프로필에 언급된 바와 같이, 아래의 사용.
Apache 2.0
라이센스도 허용됩니다(귀속 필요 없음).
이것은 데이터 개체가 아닌 스위프트 바이트 배열에서 작동하기 때문에 OP의 질문에 실제로 답하지 않습니다.그리고 다른 대답들보다 훨씬 더 큽니다.그러나 String(형식: )을 사용하지 않기 때문에 더 효율적이어야 합니다.
어쨌든 누군가가 이걸 유용하게 여기길 바라면서...
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 the byteArrayToHexString() method
let byteArray : [UInt8] = [ 0x25, 0x99, 0xf3 ]
assert(StringMisc.byteArrayToHexString(byteArray) == "2599F3")
여기서 다른 답변과 약간 다릅니다.
extension DataProtocol {
func hexEncodedString(uppercase: Bool = false) -> String {
return self.map {
if $0 < 16 {
return "0" + String($0, radix: 16, uppercase: uppercase)
} else {
return String($0, radix: 16, uppercase: uppercase)
}
}.joined()
}
}
그러나 기본 XCT 테스트 + 측정 설정에서는 시도한 4개 중 가장 빨랐습니다.
1000바이트의 (동일한) 랜덤 데이터를 각각 100번씩 경험하는 것:
이상: 시간평균: 0.028초, 상대표준편차: 1.3%
마틴 R:시간평균: 0.037초, 상대표준편차: 6.2%
자이프랙스: 시간평균: 0.032초, 상대표준편차: 2.9%
닉 무어:시간평균: 0.039초, 상대표준편차: 2.0%
검정을 반복하면 동일한 상대 결과가 반환됩니다.(닉과 마틴스는 가끔 서로 교환하기도 합니다.)
편집: 요즘은 다음을 사용합니다.
var hexEncodedString: String {
return self.reduce(into:"") { result, byte in
result.append(String(byte >> 4, radix: 16))
result.append(String(byte & 0x0f, radix: 16))
}
}
아마 가장 빠르지는 않겠지만, data.map({ String($0, radix: 16) }).joined()
그 일을 합니다.
댓글에 언급된 것처럼, 이 솔루션은 결함이 있었습니다.
언급URL : https://stackoverflow.com/questions/39075043/how-to-convert-data-to-hex-string-in-swift
'source' 카테고리의 다른 글
Google 차트 창 크기 조정/축소 (0) | 2023.09.18 |
---|---|
특성 값으로 요소를 선택하려면 XPath (0) | 2023.09.18 |
@EnableWebSecurity와 @EnableWebMvcSecurity의 차이점은 무엇입니까? (0) | 2023.09.18 |
자바스크립트/jQuery에서 문자열의 픽셀 길이를 결정하시겠습니까? (0) | 2023.09.18 |
jQuery: 변수를 선택기로 사용 (0) | 2023.09.18 |