解码字符串,包括 Swift 中的 '\xc3\xa6' 等 utf8 文字?
Decoding strings including utf8-literals like '\xc3\xa6' in Swift?
跟进我之前 关于 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([=12=], 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')
在Swift5中是否有类似的方法来解码这些字符串?
首先将解码代码添加到字符串扩展中作为计算 属性(或创建一个函数)
extension String {
var decodeUTF8: String {
let bytes = self.components(separatedBy: "\x")
.dropFirst()
.compactMap { UInt8([=10=], 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([=10=], 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
捕获了一个失败的转换。您可能希望在生产代码中以更好的方式处理此问题(尽管,除非正则表达式错误,否则它永远不会发生!)。这里需要有某种形式的中断或错误抛出,否则你有一个无限循环。
跟进我之前
已确定您可以像这样从仅包含 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([=12=], 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')
在Swift5中是否有类似的方法来解码这些字符串?
首先将解码代码添加到字符串扩展中作为计算 属性(或创建一个函数)
extension String {
var decodeUTF8: String {
let bytes = self.components(separatedBy: "\x")
.dropFirst()
.compactMap { UInt8([=10=], 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([=10=], 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
捕获了一个失败的转换。您可能希望在生产代码中以更好的方式处理此问题(尽管,除非正则表达式错误,否则它永远不会发生!)。这里需要有某种形式的中断或错误抛出,否则你有一个无限循环。