解码包含 utf8 文字的字符串,例如 Swift 中的'xc3xa6'?



关于UTF-8文本的前一个线程的后续问题:

已经确定,您可以从这样的字符串中解码UTF-8文本,该字符串只包括UTF-8文本:

let s = "\xc3\xa6"
let bytes = s
.components(separatedBy: "\x")
// components(separatedBy:) would produce an empty string as the first element
// because the string starts with "x". We drop this
.dropFirst() 
.compactMap { UInt8($0, radix: 16) }
if let decoded = String(bytes: bytes, encoding: .utf8) {
print(decoded)
} else {
print("The UTF8 sequence was invalid!")
}

但是,只有当字符串仅包含UTF-8文本时,这才有效。当我获取一个包含UTF-8文字的Wi-Fi名称列表时,我该如何解码整个字符串?

示例:

let s = "This is a WiFi Name \xc3\xa6 including UTF-8 literals \xc3\xb8"

预期结果:

print(s)
> This is a WiFi Name æ including UTF-8 literals ø

在Python中,有一个简单的解决方案:

contents = source_file.read()
uni = contents.decode('unicode-escape')
enc = uni.encode('latin1')
dec = enc.decode('utf-8')

Swift 5中有类似的解码这些字符串的方法吗?

首先将解码代码添加到字符串扩展中作为计算属性(或创建函数(

extension String {
var decodeUTF8: String {
let bytes = self.components(separatedBy: "\x")
.dropFirst()
.compactMap { UInt8($0, radix: 16) }
return String(bytes: bytes, encoding: .utf8) ?? self
}
}

然后使用正则表达式并使用while循环进行匹配以替换所有匹配值

while let range = string.range(of: #"(\x[a-f0-9]{2}){2}"#, options: [.regularExpression, .caseInsensitive]) {
string.replaceSubrange(range, with: String(string[range]).decodeUTF8)
}

据我所知,没有本地Swift解决方案。为了使其在调用站点上看起来像Python版本一样紧凑,您可以在String上构建一个扩展来隐藏复杂性

extension String {
func replacingUtf8Literals() -> Self {
let regex = #"(\x[a-zAZ0-9]{2})+"#

var str = self

while let range = str.range(of: regex, options: .regularExpression) {
let literalbytes = str[range]
.components(separatedBy: "\x")
.dropFirst()
.compactMap{UInt8($0, radix: 16)}
guard let actuals = String(bytes: literalbytes, encoding: .utf8) else {
fatalError("Regex error")
}
str.replaceSubrange(range, with: actuals)
}
return str
}
}

这可以让你调用

print(s.replacingUtf8Literals()). 
//prints: This is a WiFi Name æ including UTF-8 literals ø

为了方便起见,我用fatalError捕获了一个失败的转换。您可能希望在生产代码中以更好的方式处理此问题(尽管,除非regex是错误的!(。这里需要抛出某种形式的break或error,否则就会有一个无限循环。

最新更新