Unicode 使用(核心)基础折叠/规范化连接(例如,至ae)

Unicode 使用(核心)基础折叠/规范化连接(例如,至ae),unicode,transform,core-foundation,foundation,Unicode,Transform,Core Foundation,Foundation,我正在编写一个助手,它对输入字符串执行许多转换,以便创建该字符串的搜索友好表示形式 考虑以下场景: 德语或法语文本的全文搜索 数据存储中的条目包含 Müller Großmann chiingletòn Bjørk Æreograme 在这种情况下,搜索应该是模糊的 ull,ll等匹配Müller Gros,groß等匹配großmann cin等匹配chiingletòn bjö,bjo等。匹配bjørk aereo等匹配Æreograme 到目前为止,我在案例(1)、(3)和(4)中

我正在编写一个助手,它对输入字符串执行许多转换,以便创建该字符串的搜索友好表示形式

考虑以下场景:

  • 德语或法语文本的全文搜索
  • 数据存储中的条目包含
  • Müller
  • Großmann
  • chiingletòn
  • Bjørk
  • Æreograme
  • 在这种情况下,搜索应该是模糊的
  • ull
    ll
    等匹配
    Müller
  • Gros
    groß
    等匹配
    großmann
  • cin
    等匹配
    chiingletòn
  • bjö
    bjo
    等。匹配
    bjørk
  • aereo
    等匹配
    Æreograme
到目前为止,我在案例(1)、(3)和(4)中都取得了成功

我搞不清楚的是,如何处理(2)和(5)

到目前为止,我已经尝试了以下方法,但没有效果:

CFStringNormalize() // with all documented normalization forms
CFStringTransform() // using the kCFStringTransformToLatin, kCFStringTransformStripCombiningMarks, kCFStringTransformStripDiacritics
CFStringFold() // using kCFCompareNonliteral, kCFCompareWidthInsensitive, kCFCompareLocalized in a number of combinations -- aside: how on earth do I normalize simply _composing_ already decomposed strings??? as soon as I pack that in, my formerly passing tests fail, as well...
我略读了一遍,但没有在上面投入太多,原因很明显

我知道我可以通过转换成大写然后再转换回小写来捕获大小写(2),这将在这个特定应用程序的领域内工作。然而,我对从更基本的层面解决这个问题感兴趣,希望也能考虑区分大小写的应用程序


任何提示都将不胜感激

祝贺你,你发现了文本处理中最痛苦的一点

首先,如果你还没有看过的话,它们是这类事情不可或缺的资源

问题的一部分在于,您试图做一些几乎正确的事情,这些事情适用于您所关心的所有语言/区域设置,而Unicode更关心的是在单一语言区域设置中显示字符串时做正确的事情

对于(2),
ß
从我能找到的最早的CaseFolding.txt()起,就被规范地折叠为
ss
CFStringFold()
-[NSString stringByFoldingWithOptions:
应该做正确的事情,但如果做不到这一点,一个“与语言环境无关的”
s.upper().lower()
似乎为所有输入提供了一个合理的答案(并且还处理臭名昭著的“Turkish I”)

对于(5),您的运气有点不好:Unicode 6.2似乎不包含从Æ到AE的规范映射,并且从“字母”到“连字”再到(U+00C6在1.0中是
拉丁大写字母AE
,在1.1中是
拉丁大写连字AE
,在2.0中是
拉丁大写字母AE
)。您可以在NamesList.txt中搜索“ligature”,并添加一些特殊情况

注:

  • CFStringNormalize()
    不执行您想要的操作。您确实希望在将字符串添加到索引之前对其进行规范化;我建议在其他处理的开始和结束时使用NFKC
  • CFStringTransform()
    也不能完全满足您的需要;所有脚本都是“拉丁语”
  • CFStringFold()
    取决于顺序:组合由
    kCFCompareDiacriticInsensitive
    剥离,但由
    KCFCompareCaseCensitive
    转换为小写iota。“正确”的做法似乎是先折叠案例,然后再折叠其他案例,尽管从语言学角度来说,将其剥离可能更有意义
  • 您几乎肯定不想使用
    kCFCompareLocalized
    ,除非您希望在每次区域设置更改时重新生成搜索索引

其他语言的读者注意:检查您使用的函数是否不依赖于用户当前的语言环境!Java用户应该使用类似于
s.toUpperCase(Locale.ENGLISH)
,.NET用户应该使用
s.ToUpperInvariant()
。如果您确实需要用户的当前区域设置,请显式指定它。

我在字符串上使用了以下扩展,它似乎工作得很好

/// normalized version of string for comparisons and database lookups.  If normalization fails or results in an empty string, original string is returned.
var normalized: String? {
    // expand ligatures and other joined characters and flatten to simple ascii (æ => ae, etc.) by converting to ascii data and back
    guard let data = self.data(using: String.Encoding.ascii, allowLossyConversion: true) else {
        print("WARNING: Unable to convert string to ASCII Data: \(self)")
        return self
    }
    guard let processed = String(data: data, encoding: String.Encoding.ascii) else {
        print("WARNING: Unable to decode ASCII Data normalizing stirng: \(self)")
        return self
    }
    var normalized = processed

    //  // remove non alpha-numeric characters
    normalized = normalized.replacingOccurrences(of: "?", with: "") // educated quotes and the like will be destroyed by above data conversion
    // strip appostrophes
    normalized = normalized.replacingCharacters(in: "'", with: "")
    // replace non-alpha-numeric characters with spaces
    normalized = normalized.replacingCharacters(in: CharacterSet.alphanumerics.inverted, with: " ")
    // lowercase string
    normalized = normalized.lowercased()

    // remove multiple spaces and line breaks and tabs and trim
    normalized = normalized.whitespaceCollapsed

    // may return an empty string if no alphanumeric characters!  In this case, use the raw string as the "normalized" form
    if normalized == "" {
        return self
    } else {
        return normalized
    }
}
+1太棒了!我已经得出结论,我永远也得不到这个问题的答案。我不再处理这个问题了,所以我需要一些时间来充分理解这个问题——我想我周末有点阅读要做!