【问题标题】:How can I find a subsequence in a &[u8] slice?如何在 &[u8] 切片中找到子序列?
【发布时间】:2016-03-09 20:06:21
【问题描述】:

我在二进制缓冲区上有一个&[u8] 切片。我需要解析它,但我想使用的很多方法(例如str::find)似乎在切片上不可用。

我已经看到我可以通过使用 from_utf8_unchecked() 将缓冲区切片和我的模式转换为 str 但这似乎有点危险(而且也很hacky)。

如何在这个切片中找到一个子序列?我实际上需要模式的索引,而不仅仅是零件的切片视图,所以我认为split 不会起作用。

【问题讨论】:

  • 有兴趣将Pattern 的概念扩展到任意切片:commentRFC

标签: rust


【解决方案1】:

这是一个基于windows 迭代器的简单实现。

fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {
    haystack.windows(needle.len()).position(|window| window == needle)
}

fn main() {
    assert_eq!(find_subsequence(b"qwertyuiop", b"tyu"), Some(4));
    assert_eq!(find_subsequence(b"qwertyuiop", b"asd"), None);
}

find_subsequence 函数也可以设为泛型:

fn find_subsequence<T>(haystack: &[T], needle: &[T]) -> Option<usize>
    where for<'a> &'a [T]: PartialEq
{
    haystack.windows(needle.len()).position(|window| window == needle)
}

【讨论】:

  • 非常好。我想我基本上是用两个嵌套的 for 循环手动完成的。我正在寻找的子数组都非常小,所以像 KMP 这样更复杂的东西对我的问题毫无用处。
  • 虽然这是一个简短而好的解决方案,但请注意该算法在 O(|haystack| * |needle|) 中运行。在大多数情况下这无关紧要,但对于更高级和(渐近)更快的算法,请参阅String searching algorithm (Wikipedia)
  • 这最终会慢得令人无法接受。 windows().position() 比两个嵌套循环慢 100 倍。
  • @JasonN 100x 听起来很极端。您确定要使用优化进行编译吗?
  • @JasonN 你是如何处理两个嵌套循环的?这个解决方案应该编译成与一个循环相同的东西。
【解决方案2】:

我发现memmem crate 对这项任务很有用:

use memmem::{Searcher, TwoWaySearcher};

let search = TwoWaySearcher::new("dog".as_bytes());
assert_eq!(
    search.search_in("The quick brown fox jumped over the lazy dog.".as_bytes()),
    Some(41)
);

【讨论】:

    【解决方案3】:

    我认为标准库不包含此功能。一些 libcs​​ 有 memmem,但目前 libc 板条箱没有包装它。但是,您可以使用twoway craterust-bio 也实现了一些模式匹配算法。所有这些都应该比使用haystack.windows(..).position(..)更快

    【讨论】:

      【解决方案4】:

      Regex on bytes 怎么样?这看起来非常强大。请参阅此Rust playground demo

      extern crate regex;
      
      use regex::bytes::Regex;
      
      fn main() {
          //see https://doc.rust-lang.org/regex/regex/bytes/
      
          let re = Regex::new(r"say [^,]*").unwrap();
      
          let text = b"say foo, say bar, say baz";
      
          // Extract all of the strings without the null terminator from each match.
          // The unwrap is OK here since a match requires the `cstr` capture to match.
          let cstrs: Vec<usize> =
              re.captures_iter(text)
                .map(|c| c.get(0).unwrap().start())
                .collect();
      
          assert_eq!(cstrs, vec![0, 9, 18]);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-12-22
        • 2016-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-20
        相关资源
        最近更新 更多