Swift 子字符串范围函数在特定情况下找不到匹配项

问题描述 投票:0回答:1

我正在尝试查找泰米尔语字符串中的子字符串范围。对于以 Unicode 修饰符字形“Tamil pulli”(U+0BCD)结尾的子字符串,范围函数似乎尤其存在问题。

当在较大字符串中的单词末尾找到子字符串匹配时,范围函数将按预期工作。但是,当子字符串匹配出现在较长字符串中的单词中间时,该函数找不到匹配项。

这是一个简短的示例,其中包含两个子字符串 ss1 和 ss2 以及字符串 s。虽然“ss2”在较大的字符串中正确匹配,但 ss1 找不到匹配项。我们用几个单词复制了这一点,并确认当子字符串以修饰符字形 (U+0BCD) 结尾并且它出现在字符串中较大单词的中间时,不会找到子字符串匹配的行为。所有 3 个变量的 Unicode 代码点均正确显示,如下面的输出所示。

var s = "அவர்கள்"
var ss1 = "அவர்"
var ss2 = "கள்"
if let r2 = s.range(of: ss2)?.lowerBound {
    print("INFO: Success! Found the substring",s[r2...],"in string",s)
} else {
    print("ERROR: substring",ss2,"not found in string",s)
}

if let r1 = s.range(of: ss1)?.upperBound {
    print(s[..<r1])
} else {
    print("ERROR: substring",ss1,"not found in string",s)
}
print(s.unicodeScalars.map { $0 })
print(ss1.unicodeScalars.map { $0 })
print(ss2.unicodeScalars.map { $0 })

输出:
信息:成功!在字符串 அவர்கள் 中找到子字符串 கள்
错误:在字符串 அவர்கள் 中找不到子字符串 அவர்
["\u{0B85}"、"\u{0BB5}"、"\u{0BB0}"、"\u{0BCD}"、"\u{0B95}"、"\u{0BB3}"、" \u{0BCD}"]
[“\u{0B85}”、“\u{0BB5}”、“\u{0BB0}”、“\u{0BCD}”]
[“\u{0B95}”、“\u{0BB3}”、“\u{0BCD}”]

更新:
泰米尔语中的修饰语添加或删除基本字符的元音,从这个意义上说,它们与变音符号不同。在某些情况下,它们甚至完全改变了基本字符的形式。例如,以下是添加到基本字符 ர (U+0BB0) 的一些修饰符(及其代码点)的结果。

ர - ra(辅音元音;无修饰语)
ரா - raa (U+0BBE)
ரி - ri (U+0BBF)
ரு - ru(基本字符现在无法识别;U+0BC1)
ரெ - 重新 (U+0BC6)
ரை - rai (U+0BC8)
ரொ - ro (U+0BCA)
ர் - r(纯辅音;U+0BCD)

因此,虽然@Larme 下面的解决方案有效,但因为它将 ர் 中的点字形视为变音符号,所以它会导致我们无效/不需要的匹配。

s = "அவர்கள்", ss1 = "அவர்" - 匹配 அவர்
s = "அவரகள்", ss1 = "அவர்" - 匹配 அவர (不需要)
s = "அவராகள்", ss1 = "அவர்" - 匹配 அவரா(不需要)
s = "அவரிகள்", ss1 = "அவர்" - 匹配 அவர (不需要)
s = "அவரைகள்", ss1 = "அவர்" - 匹配 அவர (不需要)

swift unicode range substring modifier
1个回答
0
投票

如果 localizedStandardRange 不能满足您的要求,您将需要实现自己的方法来查找子字符串的范围。也许像这个post所示:

extension Collection where Element: Equatable {
    func findRange<C: Collection>(of collection: C) -> Range<Index>? where C.Element == Element {
        guard !collection.isEmpty else { return nil }
        let size = collection.count
        var range: Range<Index>!
        guard let _ = indices.dropLast(size-1).first(where: {
            range = $0..<index($0, offsetBy: size)
            return self[range].elementsEqual(collection)
        }) else {
            return nil
        }
        return range
    }
    func containsSubsequence<C: Collection>(_ collection: C) -> Bool where C.Element == Element  {
        guard !collection.isEmpty else { return false }
        let size = collection.count
        for i in indices.dropLast(size-1) where self[i..<index(i, offsetBy: size)].elementsEqual(collection) {
            return true
        }
        return false
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.