如何在任意位置匹配多个字节?

时间:2017-06-29 09:53:43

标签: rust

我必须匹配fn f(s: &[u8]) { if Some(&b'A') == s.get(5) && Some(&b'X') == s.get(16) && (Some(&b'B') == s.get(17) || Some(&b'C') == s.get(18)) { } else if Some(&b'1') == s.get(4) && Some(&b'2') == s.get(7) && Some(&b'D') == s.get(10) { } } 中的字节,并希望简化我的代码。 现在它看起来像这样:

macro_rules! m {
    ($ch:expr, $pos:expr) => {{
        Some(&$ch) == line.get($pos)
    }};
    ($ch1:expr, $ch2:expr, $pos:expr) => {{
        Some(&$ch1) == line.get($pos) || Some(&$ch2) == line.get($pos)
    }}
}

我知道nom,但它并没有使这个特例变得更简单,尽管我之后使用nom来提取匹配后的数据。

所以我第一次尝试简化代码就是编写这些宏:

macro_rules! match_several {
    ($($ch:expr, $pos:expr),*, $last_ch:expr, $last_pos:expr) => {{
        (Some(&$ch) == line.get($pos) &&)* Some(&$last_ch) == line.get($last_pos)
    }}
}

它减少了代码大小,并减少了犯错的可能性, 但我想要更多:

 error: local ambiguity: multiple parsing options: built-in NTs expr ('last_ch') or expr ('ch').
  --> lib.rs:45:32
   |
45 |     if match_several!(b'P', 4, b' ', 5, b'A', 12) {
   |        ------------------------^^^^-------------- in this macro invocation

但是编译器给出了这个错误:

let line: &'static [u8] = b"0123P 6789ABA";
println!("match result {:?}", match_several!(b'P', 4, b' ', 5, b'A', 12));

测试代码:

public interface NService {
    @GET("/computers?p=2")
    Call<Model> getItems();
}

2 个答案:

答案 0 :(得分:4)

将宏更改为:

macro_rules! match_several {
    ($($ch:expr, $pos:expr),*) => {{
        $(Some(&$ch) == line.get($pos))&&*
    }}
}

请注意,我使用&&作为分隔符,我摆脱了$last_ch$last_pos。这是有效的,因为您可以使用除<{1}}或+之外的任何令牌作为分隔符,而*是一个令牌。

答案 1 :(得分:3)

在这种情况下,重复模式可以被抽象为一些有用的函数。

#[inline]
fn all_match(s: &[u8], pattern: &[(u8, usize)]) -> bool {
    pattern.into_iter().all(|&(b, p)| s.get(p) == Some(&b))
}

#[inline]
fn any_match(s: &[u8], pattern: &[(u8, usize)]) -> bool {
    pattern.into_iter().any(|&(b, p)| s.get(p) == Some(&b))
}

fn f(s: &[u8]) {
    let and_pattern = [(b'A', 5), (b'X', 16)];
    let or_pattern = [(b'B', 17), (b'C', 18)];
    if all_match(s, &and_pattern) 
       && any_match(s, &or_pattern) {
    } else if all_match(s, &[(b'1', 4), (b'2', 7), (b'D', 10)]) {
    }
}