我正在尝试查找泰米尔语字符串中的子字符串范围。对于以 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 = "அவர்" - 匹配 அவர (不需要)
如果 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
}
}