SwiftのStringは生半可な知識で挑むといろいろ面食らいます。
度々「そもそも文字とは何か」という概念的な問いまで遡っては、「今はそんなことを考えたいわけではないんだ!」と我に帰ったりしがちです。(Swiftあるある)
練習としてStringの流儀に沿った形での16進数文字列 -> Data変換を考えてみました。
ポイントは
- 全体的にUnicodeScalarViewを扱う
- Swiftの文字列の内部表現に一番近い(低コスト)
- スペースや改行のスキップ判定にCharacterSetを使いやすい
- 全体の文字数をカウントしたり、毎回先頭からのオフセットをとるなどコストの高い計算を避ける
- 末尾再帰による最適化(を期待)
- 1byte16進数のパースはUInt8(_:string, radix:int)におまかせしている
- さらに最適化をめざすならここを自作してstringを経由しない形にしてもいいかも
String+hex.swift
import Foundation
// "".hexData.hexString //
// "01".hexData.hexString // 01
// "ab".hexData.hexString // ab
// "abff 99".hexData.hexString // abff99
// "abff\n99".hexData.hexString // abff99
// "abzff 99".hexData.hexString // ab
// "abf ff 99".hexData.hexString // ab
// "abf".hexData.hexString // ab
fileprivate func convertHex(_ s: String.UnicodeScalarView, i: String.UnicodeScalarIndex, appendTo d: [UInt8]) -> [UInt8] {
let skipChars = CharacterSet.whitespacesAndNewlines
guard i != s.endIndex else { return d }
let next1 = s.index(after: i)
if skipChars.contains(s[i]) {
return convertHex(s, i: next1, appendTo: d)
} else {
guard next1 != s.endIndex else { return d }
let next2 = s.index(after: next1)
let sub = String(s[i..<next2])
guard let v = UInt8(sub, radix: 16) else { return d }
return convertHex(s, i: next2, appendTo: d + [ v ])
}
}
extension String {
/// Convert Hexadecimal String to Array<UInt>
/// "0123".hex // [1, 35]
/// "aabbccdd 00112233".hex // 170, 187, 204, 221, 0, 17, 34, 51]
var hex : [UInt8] {
return convertHex(self.unicodeScalars, i: self.unicodeScalars.startIndex, appendTo: [])
}
/// Convert Hexadecimal String to Data
/// "0123".hexData /// 0123
/// "aa bb cc dd 00 11 22 33".hexData /// aabbccdd 00112233
var hexData : Data {
return Data(convertHex(self.unicodeScalars, i: self.unicodeScalars.startIndex, appendTo: []))
}
}
extension Data {
var hexString : String {
return self.reduce("") { (a : String, v : UInt8) -> String in
return a + String(format: "%02x", v)
}
}
}
参考
なぜSwiftの文字列APIは難しいのか | プログラミング | POSTD
http://postd.cc/why-is-swifts-string-api-so-hard/
String - Swift Standard Library | Apple Developer Documentation
https://developer.apple.com/reference/swift/string