【问题标题】:How to compare string equality which contains unicode characters in swift?如何在swift中比较包含unicode字符的字符串相等性?
【发布时间】:2020-07-28 06:22:23
【问题描述】:

在我的应用程序中,我试图比较来自具有多个 JSON 文件的 repo 的值,每个 JSON 文件将具有某个国家/地区的值作为字典,例如:

{cz: "Doplňky k Apple TV"
 dk: "Apple TVtilbehør" }  //string1 == "Doplňky k Apple TV"

类似的,我有一个本地 plist,它也将具有相同国家/地区的 dict,例如:

{cz: "Doplňky*k*Apple*TV"
 dk: "*Apple*TV*Tilbehør*" } //string2 == "Doplňky*k*Apple*TV"

所以,基本上我需要比较每个国家/地区的值,然后只向用户显示差异。

因此,在这种情况下,JSON 文件 (string1) 和本地 plist(string2) 中的 cz 值是相同的,除了 string2 中有星号。当我删除星号并比较字符串时,它们仍然不匹配,因为 Doplňky k Apple TVstring1 中的 Apple 之后有不可见的 unicode 空间,看起来像一个空格。

下面是我实现逻辑的代码:

if string2.replaceString(["*", "\u{00a0}"], " ").trimmingCharacters(in: .whitespaces) == string1.replacingOccurrences(of: "\u{00a0}", with: " "){
  //Do something
}

【问题讨论】:

  • 我正在尝试从 string2 中删除 unicode 字符,这不是这样吗?我不知道如何从字符串中删除 unicode,它可能出现在我不知道的 string2 中的任何位置。
  • 其实我只想排除 UNICODES,因为 string2 可能包含数字、货币符号等,我想保留这些东西。
  • 在 SO 上很少看到捷克语的例子 :) 你能描述一下你想要做什么,高级描述吗?这是一些用户输入,并且您正在将其与您的字符串匹配吗?还是其他处理?询问上下文,因为 regex 匹配两个字符串匹配相似字符串,...不是很清楚。您可能需要对它们进行规范化(不同的代码点可以产生相同的值,...),...
  • 我会重新构建问题并在某个时间发布以提供更清晰的信息。
  • @zrzka 编辑了问题以更清楚。

标签: swift macos cocoa


【解决方案1】:

Doplňky k Apple TV 字符串看起来像是来自 Apple 网站。当我在他们的网站上查看时,这个字符串包含AppleTV 之间的NO-BREAK SPACE (U+00A0)。它是一个空格字符,但不等于普通的SPACE (U+0020)。

"Doplňky k Apple\u{00a0}TV" == "Doplňky k Apple TV" // false

首先要指定 - 重要吗?我们是否应该平等对待?

然后你有Apple TVtilbehør & *Apple*TV*Tilbehør* 字符串。是故意错字吗?还是Apple TVtilbehør 应该是Apple TV Tilbehør?让我们假设这是为了测试您的比较而故意拼写错误。

接下来,*Apple*TV*Tilbehør* 字符串中的这些*(在开头/结尾)是...? 第二件事要说明——我们应该忽略它们吗?它们代表空格吗?

接下来是Unicode equivalence。您想如何比较这两个字符串? Swift 在这里为您提供帮助 (source):

使用等于运算符 (==) 或关系运算符(如 <>=)比较字符串是否相等始终使用 Unicode 规范表示。结果,字符串的不同表示比较相等。

"Cafe\u{301}" == "Café" // true

其他国家呢?像德国一样,Straße 等于 Strasse要指定的第三件事 - 我们应该如何处理这些字符串?

如您所见,有很多事情需要考虑。你有规格吗?跟着它。没有规范?你的算法迟早会停止工作。

游乐场

我冒昧地自行指定了所有这些内容:

  • 所有空格都相等
  • * 开头/结尾无所谓(忽略)
  • Straße 不等于 Strasse

示例代码:

import Foundation

let json = [
    // U+00A0 is NO-BREAK SPACE which looks like a normal space (U+0020)
    "cz": "Doplňky k Apple\u{00a0}TV",
    "dk": "Apple TV Tilbehør",
    "en": "Hello",
    "de": "Straße",
    "fr": "Expos\u{00E9}" // Exposé
]

let plist = [
    "cz": "Doplňky*k*Apple*TV",
    "dk": "*Apple*TV*Tilbehør*",
    "es": "Hola",
    "de": "Strasse",
    "fr": "Expose\u{0301}" // Exposé
]

let jsonKeys = Set(json.keys)
let plistKeys = Set(plist.keys)
let commonKeys = jsonKeys.intersection(plistKeys)
let keysMissingInJson = plistKeys.subtracting(jsonKeys)
let keysMissingInPlist = jsonKeys.subtracting(plistKeys)

print("Languages missing in JSON: \(keysMissingInJson.count)")
keysMissingInJson.forEach { key in
    print(" - \(key)")
}

print("Languages missing in PLIST: \(keysMissingInPlist.count)")
keysMissingInPlist.forEach { key in
    print(" - \(key)")
}

let differentValueKeys: [String] = commonKeys.compactMap { key in
    guard let initialJsonValue = json[key], let initialPlistValue = plist[key] else {
        fatalError("Fix commonKeys")
    }
    
    // Replace all whitespace characters with a normal space
    let jsonValue = String(
        initialJsonValue.map { $0.isWhitespace ? " " : $0 }
    )
    
    let plistValue = initialPlistValue
        // Replace all * with a normal whitespace
        .replacingOccurrences(of: "*", with: " ")
        // Trim all whitespace characters from the beginning/end
        .trimmingCharacters(in: .whitespaces)
    
    return jsonValue == plistValue ? nil : key
}

print("Different values: \(differentValueKeys.count)")
differentValueKeys.forEach { key in
    print(" - \(key): JSON(\(json[key]!)) PLIST(\(plist[key]!))")
}

输出:

Languages missing in JSON: 1
 - es
Languages missing in PLIST: 1
 - en
Different values: 1
 - de: JSON(Straße) PLIST(Strasse)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-10
    • 1970-01-01
    • 2015-06-09
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 2022-07-22
    • 1970-01-01
    相关资源
    最近更新 更多