Swift 4 base64字符串到数据不工作,因为字符串包含;“不完整”;表情符号

Swift 4 base64字符串到数据不工作,因为字符串包含;“不完整”;表情符号,swift,character-encoding,base64,iso,Swift,Character Encoding,Base64,Iso,我来自这篇文章,但与此同时,我能够将问题隔离为10个字符的字符串 简短介绍:一个用户的应用程序没有显示任何内容。用TextWrangler查看他6kb的纯文本数据,我发现了两个红色问号 我试图在问号周围切割一些base64编码的数据块,并将它们转换为不起作用的数据。当我把红色问号的部分从块中去掉后,它似乎又开始工作了。请看我下面的游乐场示例: //those do NOT work let toEndBracket = "ACAAKgBVAFMAQQAqACAnlgAg2DwAIgB9AF0A

我来自这篇文章,但与此同时,我能够将问题隔离为10个字符的字符串

简短介绍:一个用户的应用程序没有显示任何内容。用TextWrangler查看他6kb的纯文本数据,我发现了两个红色问号

我试图在问号周围切割一些base64编码的数据块,并将它们转换为不起作用的数据。当我把红色问号的部分从块中去掉后,它似乎又开始工作了。请看我下面的游乐场示例:

//those do NOT work
let toEndBracket = "ACAAKgBVAFMAQQAqACAnlgAg2DwAIgB9AF0A" // *USA* ' <"}]//
let toMidBracket = "ACAAKgBVAFMAQQAqACAnlgAg2DwAIgB9"     // *USA* ' <"}//
let toCarrot =     "ACAAKgBVAFMAQQAqACAnlgAg2DwA"         // *USA* ' <//
let toSpace =      "ACAAKgBVAFMAQQAqACAnlgAg"             // *USA* ' //

//but this one WORKS
let toApostrophe = "ACAAKgBVAFMAQQAqACAn"                 // *USA* '//
//(basically the last one is without the space before the carrot, I've added the slashes after it to emphasize that)
//clear strings taken from https://www.base64decode.org/ using the UTF-8 setting WITHOUT "Live mode".

if let textData = Data(base64Encoded: toApostrophe) {
    print("Data created")   //works for all of them
    print(textData)
    if let decodedString = String(data: textData, encoding: .utf8) {
        print("WORKED!!!")  //only happens for the toApostrophe
        print(decodedString)
    } else {
        print("DID NOT WORK")
    }
}
这是它栖息地的原始文本,一条带有表情符号的信使信息说“USA”,因此在我的示例文本中是“USA”,我怀疑是表情符号使其破裂:

如果有人能告诉我如何“清理”base64字符串,使其再次转换为数据,我将不胜感激。这也可能是由于一些表情符号的一些奇怪的编码,但在大多数情况下,应用程序接收和显示带有表情符号的内容很好


我终于弄明白了为什么会这样。对于我的问题,这不是一个快速的解决方案,但现在它至少有了一些意义。对于新内容的预览,我剪切字符串以匹配浏览器的视口。这位不幸的用户在显示屏挡板的边缘有美国国旗表情符号。我从来没有想到过由多个字母组成的表情符号和JavaScript的
substring()
将其斩首。看一看图片,这说明了角色来自何处等

对于如何避免/忽略/抓住Swift中的这些问题,我仍然希望您能给出一个答案,但对于每一个遇到这个问题的可怜的灵魂,我希望您能无意中发现这条线索

(其中一些是出于评论,但试图将其结合起来并描述解决方案。)

首先,您的字符串不是UTF-8。它们是UTF-16或格式不正确的UTF-16。有时UTF-16恰好可以解释为UTF-8,但当它是UTF-8时,将有空字符散布在字符串中。在您的“工作”示例中,它实际上不工作

let toApostrophe = "ACAAKgBVAFMAQQAqACAn"                 // *USA* '//
if let textData = Data(base64Encoded: toApostrophe) {
    if let decodedString = String(data: textData, encoding: .utf8) {
        print(decodedString)
        print(decodedString.count)
        print(decodedString.map { $0.unicodeScalars.map { $0.value } } )
    } else {
        print("DID NOT DECODE UTF8")
    }
} else {
    print("DID NOT DECODE BASE64")
}
印刷品:

 *USA* '
15
[[0], [32], [0], [42], [0], [85], [0], [83], [0], [65], [0], [42], [0], [32], [39]]
请注意,字符串的长度是15个字符,而不是您可能期望的8个字符。这是因为它在大多数字符之间包含一个额外的不可见NULL(0)

然而,
toEndBracket
碰巧不是合法的UTF-8。以下是它的字节:

[“00”“20”“00”“2a”“00”“55”“00”“53”“00”“41”“00”“2a”“00”“20”“27”“96”“00”“20”“d8”“3c”“00”“22”“00”“7d”“00”“5d”“00”]

这在到达0xd8之前是正常的。它从位110开始,表示它是一个双字节序列的开始。但下一个字节是0x3c,它不是多字节序列的有效第二个字节(它应该以10开头,但以00开头)。所以我们无法将其解码为UTF-8。即使使用
decodeCString(uu:as:repairingInvalidDeUnits)
也无法解码此字符串,因为它被嵌入的空值填充。你必须至少使用正确的编码来解码它

但是让我们这样做。解码为UTF-16。尽管UTF-16有点无效,但至少这很接近

let toEndBracket16 = String(data: toEndBracketData, encoding: .utf16)
// " *USA* ➖ �"}]"
现在我们至少可以用这个了。但它是无效的JSON。因此,我们可以通过过滤将其去除:

let legalJSON = String(toEndBracket16.filter { $0 != "\u{FFFD}" })
// " *USA* ➖ "}]"

我真的不推荐这种方法。这是难以置信的脆弱,基于破碎的输入。修复输入。但在一个试图解析断开的输入的世界中,这些就是工具。

Base64解码完整字符串会得到
——这几乎是有效的UTF-16(但肯定不是UTF-8)。几乎意味着:
d83c
是一个高级代理,需要以下低级代理。您好,感谢您的回复!我对编码几乎一无所知,你能告诉我如何解决这个问题吗?再说一次,数据一致性根本不重要,所以如果你知道一个简单但肮脏的解决方案,它已经是完美的了。尝试编码“USA”,你能展示一下你的编码方式吗“USA@LeoDabus是的,你的声明让我再次检查JS,然后我意识到我是如何得到格式错误的JSON的。它在由多个字母组成的表情符号上使用
子字符串。现在我们知道如何重现这个问题了,多谢各位。现在,如果有人碰巧知道一种方法,使Swift不会在看到三分之一的表情符号时立即中断,这将使今天的8个小时的虫子之旅就此结束!!!很好用!事实上,我确实在几个小时前玩过替换东西的游戏,我也用utf8和16来来回回地玩过,但我从来没有想过要混合这些东西!工作非常完美,还可以序列化为JSON而不会出现问题(至少在游乐场中)。然而,我确实认真考虑了你的担心,我现在正试图清理输入数据,因为就像你说的,它非常脆弱,但现在这对我来说非常好,我非常感谢!祝你好运。建议找一个图书馆的答案是一个很好的答案。这是一个非常非常困难的问题(如果您认为标志很棘手,请尝试解码的UTF-8编码中的25个字节)
let legalJSON = String(toEndBracket16.filter { $0 != "\u{FFFD}" })
// " *USA* ➖ "}]"