如何将 UTF16 字符串解码为 Unicode 字符

How to decode a UTF16 string into a Unicode character

设备将字符串“”编码为"\uD83E\uDD1B\uD83C\uDFFD"。此字符串中表示的十六进制数字来自字符的 UTF-16 十六进制编码。 Unicode 代码点 U+1F91B, U+1F3FD 从 UTF-32 十六进制编码中获取其数字。

再往后看,在Swift中我们可以像这样写一个字面值"\u{1F91B}\u{1F3FD}",我们将得到预期的字符""。

如何从 UTF-16 十六进制字符串 "\uD83E\uDD1B\uD83C\uDFFD" 转换为“”?

我试过获取字符串并将其转换为 [UInt32] 32 位整数数组,然后使用它来创建 unicode 标量,但这仅适用于可以用单个 UTF 表示的 Unicode 字符-32 代码点。

这是我正在使用的源代码。

extension String {

    func decodeBlock() -> String {
        let strings = self.components(separatedBy: "\u")
        var scalars : [UInt32] = []
    
        var value: UInt32 = 0
        for string in strings {
            print(string)
            let scanner = Scanner(string: string)
            if scanner.scanHexInt32(&value) {
                scalars.append(value)
            }
        }
    
        let utf32chars = scalars
        var str = ""
        var generator = utf32chars.makeIterator()
        var utf32 : UTF32 = UTF32()
        var done = false
        while !done {
            let r = utf32.decode(&generator)
            switch (r) {
            case . emptyInput:
                done = true
            case .scalarValue(let val):
                str.append(Character(val))
            case .error:
                return "$"
            }
        }
        return str
    

        return self
    }
}

改编自类似问题的答案中的代码。

编码字符串的来源是org.apache.commons.lang.StringEscapeUtilsescapeJava函数,可以在here.

中找到

这有点作弊,但UTF-16恰好是NSString使用的编码,所以你可以借用NSString的方法来实现它:

extension String {
    func decodeBlock() -> String? {
        var chars = [unichar]()

        for substr in self.components(separatedBy: "\u") where !substr.isEmpty {
            if let value = UInt16(substr, radix: 16) {
                chars.append(value)
            } else {
                return nil
            }
        }

        return NSString(characters: chars, length: chars.count) as String
    }
}

if let decoded = "\uD83E\uDD1B\uD83C\uDFFD".decodeBlock() {
    print(decoded)
} else {
    print("Cannot decode")
}

这绝对是作弊,因为它只是使用 JavaScript 中的内置方法,但它确实有效。

func decode() -> String{
    // getting a JSContext
    let context = JSContext()
    let encodedString = self
    // defining a JavaScript function
    let jsFunctionText = "var decode = function(encodedString) {\n" +
        "var r = /\\u([\d\w]{4})/gi;\n" +
        " x = encodedString\n" +
        "x = x.replace(r, function (match, grp) {\n" +
        "     return String.fromCharCode(parseInt(grp, 16)); } );\n" +
        " x = unescape(x);\n" +
        " return x\n" +
    "}"
    //    print(jsFunctionText)
    context!.evaluateScript(jsFunctionText)!

    // calling a JavaScript function
    let jsFunction = context?.objectForKeyedSubscript("decode")

    let decodedValue = jsFunction?.call(withArguments: [encodedString]);
    if let decodedString = decodedValue?.toString() {
        return decodedString
    } else {
        return self
    }
}