Rust:Unicode感知字符串匹配。

5
我希望确定某个字符串是否包含特定的子字符串,同时要考虑组合字符。为了说明这个问题,让我们来看一下Rust中的以下例子:
fn main() {
    let a_umlaut = "a\u{0308}"; // "ä"
    println!("{}", a_umlaut.starts_with("a")); // true
}

基本上,上述内容显示 "ä".starts_with("a") 为true(请注意第一个“a”上面的分音符号)。我从技术层面理解这种行为的原因,但我仍希望上述代码输出 false,因为 "ä" 和 "a" 是两个不同的用户可感知字符。
是否有现有的函数/创建字符串匹配时遵守组合字符的方法?

3
通常的做法是对针和草堆进行规范化处理,然后执行搜索。这种方法的好处是可以适用于任何需要子字符串匹配的情况,而不必调整实现以考虑Unicode标准化形式。 - BurntSushi5
1
@BurntSushi5 如果我错了,请纠正我,但我认为在这种情况下规范化是没有帮助的。在上面的例子中,needle和haystack都是NFD规范化的。问题在于Rust执行的逐字符比较没有考虑到haystack中的“a”实际上不是“a”,因为它被组合字符“\u{0308}”修改了。 - DaviD.
3
你可以尝试使用这个库:https://crates.io/crates/unicode-segmentation。 - Angelicos Phosphoros
1
仅供参考,字母“ä”在Unicode中可以用两种不同的方式表示——一种是与“组合分音符”的“a”一起,另一种是单个代码点“\u{00e4}”。如果Unicode规范化最终规范化为两个代码点版本,则无法解决此问题,因此规范化针和干草堆都不是完整的解决方案。 - Sven Marnach
2
@SvenMarnach 有四种Unicode规范化形式,NFD、NKD、NFC、NKC。如果将字符串规范化为NFC或NKC,则组合字符将被合并为单个代码点。 - Stuart
显示剩余6条评论
1个回答

0
我从我的评论中展开了我的想法。这个正则表达式将匹配一个没有umlaut的“a”字符在字符串的开头。
use regex::Regex;

fn main() {
    let a_umlaut = "a\u{0308}"; // "ä"
    println!("Original string: {}", a_umlaut);
    println!("Start with regular 'a': {}", a_umlaut.starts_with("a")); // true

    let re = Regex::new(r"^a[^\u{0308}]").unwrap(); // Matches non-combined "a" at the front
    tester(&re, a_umlaut);      // "a" with umlaut behind
    tester(&re, "blessed are"); // "a" in the middle, not the front
    tester(&re, "amore!");      // "a" at the front

}

fn tester(re: &Regex, test: &str)
{
    println!("For string: '{}' with Regex: '{}', match is: {}", test, re.as_str(), re.is_match(test));
}

输出:

Original string: 
Start with regular 'a': true
For string: 'ä' with Regex: '^a[^\u{0308}]', match is: false
For string: 'blessed are' with Regex: '^a[^\u{0308}]', match is: false
For string: 'amore!' with Regex: '^a[^\u{0308}]', match is: true

Playground link

这里的想法是你可以扩展正则表达式中不想匹配的字符列表,所以任何与a组合的内容也会被列出。这个方法的问题在于这个列表可能会非常长,但如果这是一个受限制的问题,这种方法可能有效。


谢谢你的工作!我认为这个想法很好,但我不确定正则表达式是否适合,因为Unicode定义了大量的组合字符,所有这些字符都必须在正则表达式中排除(正如你已经指出的那样)。然而,可以直接使用Unicode数据表检查“a”后面的字符是否是组合字符。或者,也可以比较字形而不是字符,这应该避免组合字符的整个问题。我会尝试尽快发布一种使用这种方法的解决方案。 - DaviD.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接