Используя Swift, как вы перекодируете, а затем декодируете строку, как этот короткий скрипт в Python? - PullRequest
0 голосов
/ 18 сентября 2018

XKCD имеет некоторые проблемы с их API и странные проблемы с кодировкой.

Незначительная проблема кодирования с текстами xkcd alt в чате

Решение (в Python) состоит в том, чтобызакодировать его как latin1, затем декодировать как utf8, но как мне сделать это в Swift?

Тестовая строка:

"Be careful\u00e2\u0080\u0094it's breeding season"

Ожидаемый результат:

Be careful—it's breeding season

Python (ссылка выше):

import json
a = '''"Be careful\u00e2\u0080\u0094it's breeding season"'''
print(json.loads(a).encode('latin1').decode('utf8'))

Как это делается в Swift?

let strdata = "Be careful\\u00e2\\u0080\\u0094it's breeding season".data(using: .isoLatin1)!
let str = String(data: strdata, encoding: .utf8)

Это не работает!

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Я не смог найти ничего встроенного, но мне удалось написать это для вас.

extension String {
    func range(nsRange: NSRange) -> Range<Index> {
        return Range(nsRange, in: self)!
    }

    func nsRange(range: Range<Index>) -> NSRange {
        return NSRange(range, in: self)
    }

    var fullRange: Range<Index> {
        return startIndex..<endIndex
    }

    var fullNSRange: NSRange {
        return nsRange(range: fullRange)
    }

    subscript(nsRange: NSRange) -> Substring {
        return self[range(nsRange: nsRange)]
    }

    func convertingUnicodeCharacters() -> String {
        var string = self
        // Characters need to be replaced in groups in case of clusters
        let groupedRegex = try! NSRegularExpression(pattern: "(\\\\u[0-9a-fA-F]{1,8})+")
        for match in groupedRegex.matches(in: string, range: string.fullNSRange).reversed() {
            let groupedHexValues = String(string[match.range])
            var characters = [Character]()
            let regex = try! NSRegularExpression(pattern: "\\\\u([0-9a-fA-F]{1,8})")
            for hexMatch in regex.matches(in: groupedHexValues, range: groupedHexValues.fullNSRange) {
                let hexString = groupedHexValues[Range(hexMatch.range(at: 1), in: string)!]
                if let hexValue = UInt32(hexString, radix: 16),
                    let scalar = UnicodeScalar(hexValue) {
                    characters.append(Character(scalar))
                }
            }
            string.replaceSubrange(Range(match.range, in: string)!, with: characters)
        }
        return string
    }
}

Он в основном ищет любые значения \u<1-8 digit hex> и преобразует их в скаляры.Должно быть довольно просто ... ? Я пытался протестировать его честно, но не уверен, что он уловит каждый крайний случай.

Мой код тестирования детской площадки был просто:

let string = "Be careful\\u00e2\\u0080\\u0094-\\u1F496\\u65\\u301it's breeding season"
let expected = "Be careful\u{00e2}\u{0080}\u{0094}-\u{1f496}\u{65}\u{301}it's breeding season"
string.convertingUnicodeCharacters() == expected // true ?
0 голосов
/ 18 сентября 2018

Сначала необходимо декодировать данные JSON, затем извлечь строку и, наконец, «исправить» строку.Вот автономный пример с JSON из https://xkcd.com/1814/info.0.json:

let data = """
    {"month": "3", "num": 1814, "link": "", "year": "2017", "news": "",
    "safe_title": "Color Pattern", "transcript": "",
    "alt": "\\u00e2\\u0099\\u00ab When the spacing is tight / And the difference is slight / That's a moir\\u00c3\\u00a9 \\u00e2\\u0099\\u00ab",
    "img": "https://imgs.xkcd.com/comics/color_pattern.png",
    "title": "Color Pattern", "day": "22"}
""".data(using: .utf8)!

// Alternatively:
// let url = URL(string: "https://xkcd.com/1814/info.0.json")!
// let data = try! Data(contentsOf: url)

do {
    if let dict = (try JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any],
        var alt = dict["alt"] as? String {

        // Now try fix the "alt" string
        if let isoData = alt.data(using: .isoLatin1),
            let altFixed = String(data: isoData, encoding: .utf8) {
            alt = altFixed
        }

        print(alt)
        // ♫ When the spacing is tight / And the difference is slight / That's a moiré ♫
    }
} catch {
    print(error)
}

Если у вас есть просто строка вида

Будьте осторожны, это размножениесезона

тогда вы все равно можете использовать JSONSerialization для декодирования escape-последовательностей \uNNNN, а затем продолжить, как описано выше.

Простой пример (проверка ошибок для краткости опущена):

let strbad = "Be careful\\u00e2\\u0080\\u0094it's breeding season"
let decoded = try! JSONSerialization.jsonObject(with: Data("\"\(strbad)\"".utf8), options: .allowFragments) as! String
let strgood = String(data: decoded.data(using: .isoLatin1)!, encoding: .utf8)!
print(strgood)
// Be careful—it's breeding season
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...