【问题标题】:Finding the size of a grapheme cluster in Rust在 Rust 中查找字素簇的大小
【发布时间】:2017-07-15 15:50:21
【问题描述】:

我正在开发一个扫描器(如果你愿意,也可以使用标记器或词法分析器)。我必须遍历一个字符串切片。我找到了两种方法可以做到这一点:

首先,我可以创建一个迭代器并遍历每个字符。这是一个简化的例子:

let s = "чеllo".chars();
for c in s {
    println!("{}", c);
}

但是,如果我想向前看,那就不那么简单了:

let mut s = "чеllo = ==".chars().peekable();
loop {
    match (s.next(), s.peek()) {
        (Some('='), Some(&'=')) => { s.next(); println!("==") },
        (Some('='), _        )  => println!("="),
        (Some(c)  , _        )  => println!("{}", c),
        (None, _) => break,
    }
}

不幸的是,如果我想查看下一个字符,我似乎无法进行多次预览。

所以我可以改为使用 second 方法。我可以将字符串切片转换为字符向量。例如:

fn char_at(text: &Vec<char>, pos: usize) -> Option<char> {
    if pos < text.len() {
        Some(text[pos])
    } else {
        None
    }
}

let mut text = "чеllo = ==".chars().collect();
let mut position: usize = 0;
loop {
    match (char_at(&text, position), char_at(&text, position + 1)) {
        (Some('='), Some('=')) => { position += 1; println!("==") },
        (Some('='), _        ) => println!("="),
        (Some(c)  , _        ) => println!("{}", c),
        (None     , _        ) => break,
    }
    position += 1;
}

我现在想尝试的是第三种方法,它介于两者之间。本质上,我想要的是 Rust 之前拥有的 char_at 方法,并且我想要获得字素簇大小的能力。

如果我有这两个功能,我可以使用类似于我的字符向量方法的方法,但我可以直接在字符串切片上这样做。像这样的东西(这不是有效的 Rust 代码):

let mut text = "чеllo = ==";
let mut position: usize = 0;
loop {
    let next_char = text.char_at(position);
    let peek_char = text.char_at(position + next_char.len());
    match (next_char, peek_char)) {
        (Some('='), Some('=')) => {
            position += peek_char.len();
            println!("==")
        },
        (Some('='), _        ) => println!("="),
        (Some(c)  , _        ) => println!("{}", c),
        (None     , _        ) => break,
    }
    position += next_char.len();
}

注意:我想要的是 next_char.len()peek_char.len() 给我这些字素组成的字节数。

这是我对上述方法的理解:

  • 迭代器方法使得执行多次查看变得困难。
  • 矢量方法的成本更高一些(创建矢量的时间为 O(n),并且需要更多内存)。这些成本都不算太高,但我正在努力学习更好的方法。
  • 我讨论过的第三种方法介绍的方法会给我们这些非 Unicode 专家的人带来噩梦。

我是 Rust 的新手。所以这些是我的问题:

  • 我是否缺少完全替代的方法?
  • 是否有一种简单的方法可以向迭代器中添加多视图?
  • 能否使用我目前不知道的功能来实施我的第三种方法?

【问题讨论】:

  • 可以使用现有的解析库like nom吗?你声明语法,它会为你生成一个解析器来处理这些前瞻问题。
  • @kennytm:不确定 nom 是否有字素簇的概念……但我不太确定 OP 需要从字素簇开始。
  • @drajc:老实说,我完全不知道你想在这里做什么。所以让我们先澄清一下术语:Unicode 有代码点和字素簇的概念。代码点由一个数值表示,例如 U+1F4A9(???? 表情符号),Rust 称之为 char。字素簇是多个代码点的组合,它们合并在一起以表示单个(复杂)实体:组合代码点 U+0065 (e) 和 U+00B4 (´) 产生一个字素簇 (é)。在我看来,您根本不关心这些,而只是想要多窥视。
  • @drajc U+0065 和 U+0301 将显示为两个连续的chars。您不能将两个代码点放入一个 char 值中,chars() 一次生成一个 char
  • @drajc:如果您想一次查看多个元素,您可以使用windows(n) 迭代器,它是在底层切片上长度为n 的滑动窗口。但是,您无法匹配返回的切片。否则,我建议你看看itertools;尤其是 tuples 迭代器。

标签: rust


【解决方案1】:

str::chars()返回的迭代器,类型为str::Chars,实现了Clone。这意味着您可以创建一个与现有迭代器具有相同状态的迭代器,然后独立迭代,只需在迭代器上调用.clone()。您可以将克隆用于前瞻,并仅在您准备好前进时推进原始迭代器。

str::Chars 简单地包装了iter::Slice&lt;u8&gt;,它本身只是一对指针。因此,克隆str::Chars 只是将这两个指针复制到一个新值中,这不涉及任何内存分配。这使得克隆str::Chars 非常便宜,所以不要害羞!

fn main() {
    let mut s = "чеllo = ==".chars();
    loop {
        let mut s2 = s.clone();
        let c1 = s2.next();
        let c2 = s2.next();
        match (c1, c2) {
            (Some('='), Some('=')) => { s.next(); println!("=="); }
            (Some('='), _        ) => { s.next(); println!("="); }
            (Some(c)  , _        ) => { s.next(); println!("{}", c); }
            (None, _) => break,
        }
    }
}

【讨论】:

  • 感谢您的回复。这正是我想知道的,但我对 Rust 的缺乏经验让我很难推断出开销的数量和所有权的复杂性。
猜你喜欢
  • 2020-03-05
  • 2014-10-29
  • 2017-10-07
  • 1970-01-01
  • 2015-12-06
  • 1970-01-01
  • 2022-08-21
  • 1970-01-01
相关资源
最近更新 更多