8 个版本
0.2.7 | 2021年11月18日 |
---|---|
0.2.6 | 2021年6月20日 |
0.1.2 |
|
#2462 在 算法
44 每月下载量
42KB
675 行
Xfind
这个库为流搜索提供了快速的前后子字符串搜索器。
用法
将以下内容添加到您的 Cargo.toml 中
xfind = "0.2"
文档
许可证
在您的选择下,许可协议为 MIT-LICENSE 或 UNLICENSE。
lib.rs
:
提供流搜索的前后子字符串搜索器。
这个包是基于 memchr
构建的,它是一个针对字符串搜索进行了高度优化的例程。但与 memchr
不同,本包提供的工具直接在流(即 Read
实例)上操作,而不是在内存缓冲区中。
注意,当在已经存在于内存中的源中搜索子字符串时,此 crate 提供了没有优势,在这种情况下,考虑使用 memchr
库。此外,如果您想同时搜索多个子字符串,请查看 aho-corasick
。
复杂度
该 crate 提供的前后流搜索例程在 needle.len()
和 stream.len()
方向上保证具有最坏情况线性时间复杂度,在 needle.len()
方向上具有最坏情况常数空间复杂度。
性能
以下是搜索 767KB 的《傲慢与偏见》书籍中所有 dear
出现的基准结果。
test group_1::stream_find_iter::aho_corasick ... bench: 558,530 ns/iter (+/- 8,705)
test group_1::stream_find_iter::memchr ... bench: 89,728 ns/iter (+/- 4,979)
test group_1::stream_find_iter::xfind ... bench: 112,766 ns/iter (+/- 2,453)
test group_2::stream_rfind_iter::memchr ... bench: 613,183 ns/iter (+/- 10,610)
test group_2::stream_rfind_iter::xfind ... bench: 681,210 ns/iter (+/- 6,990)
test group_3::memory_find_iter::aho_corasick ... bench: 454,277 ns/iter (+/- 2,030)
test group_3::memory_find_iter::memchr ... bench: 21,564 ns/iter (+/- 657)
test group_3::memory_find_iter::xfind ... bench: 41,548 ns/iter (+/- 2,028)
test group_4::memory_rfind_iter::memchr ... bench: 543,737 ns/iter (+/- 4,420)
test group_4::memory_rfind_iter::xfind ... bench: 590,744 ns/iter (+/- 14,684)
-
在进行正向流搜索时,
xfind
的速度大约比memchr::memmem
(第 1 组) 慢 1.3 倍,但实际上memmem
本身是在内存缓冲区上操作的,而xfind
则直接在流上操作。主要区别在于内存使用,xfind
通过使用仅 8KB 的缓冲区来完成工作,但memmem
需要将整个文件内容读入大小为文件大小的缓冲区(在这种情况下为 767KB)。 -
xfind
在搜索内存缓冲区时没有任何优势(几乎慢2倍)(组3),因此请勿用于内存搜索。 -
当只搜索一个子串时,
xfind
在上述所有情况中(组1,3)都击败了aho-corasick
,这在一定程度上是公平的,因为aho-corasick
主要用于一次性搜索多个子串。 -
由于本质上是反向流搜索比正向流搜索慢得多(组2,4)。
xfind
和memmem
的性能相当接近,只是内存使用不同。
示例
- 检查子串是否存在于文件中。
use std::fs::File;
fn main() -> std::io::Result<()> {
let mut rdr = File::open("foo.txt")?;
let found = xfind::find(b"bar", &mut rdr).is_some();
Ok(())
}
- 获取文件中子串首次出现的10个索引。
use std::fs::File;
use std::io;
fn main() -> io::Result<()> {
let mut rdr = File::open("foo.txt")?;
let indexes = xfind::find_iter(b"bar", &mut rdr)
.take(10)
.collect::<io::Result<Vec<usize>>>()?;
println!("{:?}", indexes);
Ok(())
}
- 构建一个搜索器,并在多个流中搜索相同的针。
use std::fs::File;
use std::io;
use xfind::StreamFinder;
fn main() -> io::Result<()> {
let mut f1 = File::open("foo.txt")?;
let mut f2 = File::open("bar.txt")?;
let mut finder = StreamFinder::new(b"baz");
let found_in_f1 = finder.find(&mut f1).is_some();
let found_in_f2 = finder.find(&mut f2).is_some();
Ok(())
}
- 读取文件的最后一行,而无需将整个文件内容加载到内存中。
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
fn main() -> io::Result<()> {
let path = "foo.txt";
let mut buf = Vec::new();
read_last_line(path, &mut buf)?;
// For simplicity, we just assume the contents is valid UTF-8 and unwrap here.
println!("{}", std::str::from_utf8(&buf).unwrap());
Ok(())
}
fn read_last_line<P: AsRef<Path>>(
path: P,
buf: &mut Vec<u8>,
) -> io::Result<usize> {
let mut f = File::open(path)?;
let mut iter = xfind::rfind_iter(b"\n", &mut f)?;
let read_pos = match iter.next().transpose()? {
// if the file contains no newline, we read from the start.
None => 0,
// if the file ends with a newline, we need to perform another search.
Some(pos) if pos + 1 == iter.stream_len() => {
(iter.next().transpose()?.map(|x| x + 1)).unwrap_or(0)
}
// if the file doesn't end with a newline, then `pos + 1` is the `read_pos`.
Some(pos) => pos + 1,
};
iter.seek_to(read_pos)?;
f.read_to_end(buf)
}
依赖项
~110–255KB