Loops 当迭代文件中的行时,如何在匹配前后输出行?
我正在尝试从Rust中的GNULoops 当迭代文件中的行时,如何在匹配前后输出行?,loops,rust,Loops,Rust,我正在尝试从Rust中的GNUgrep复制-A和-B参数功能。这将打印从文件或stdin读取的匹配行前后的文本行。例如: $printf'1\n2\n3\nfoo\n4\n5\n6\n7\nfoo\n8\n9'| grep-a1-b1foo 3. 福 4. -- -- 7. 福 8. 我想要的输出将返回模式匹配之前和/或之后的n行 以just stdin为例,仅返回匹配行的简单情况很容易实现,如下所示: use std::io::{self, BufRead, BufReader, Resul
grep
复制-A
和-B
参数功能。这将打印从文件或stdin读取的匹配行前后的文本行。例如:
$printf'1\n2\n3\nfoo\n4\n5\n6\n7\nfoo\n8\n9'| grep-a1-b1foo
3.
福
4.
--
--
7.
福
8.
我想要的输出将返回模式匹配之前和/或之后的n
行
以just stdin为例,仅返回匹配行的简单情况很容易实现,如下所示:
use std::io::{self, BufRead, BufReader, Result};
fn main() {
for line in BufReader::new(io::stdin()).lines() {
match line {
Ok(l) => {
if l.contains("foo"){
println!("{}", l);
}
}
Err(e) => println!("error parsing line: {:?}", e),
}
}
}
输出:
$printf'1\n2\n3\nfoo\n4\n5\n6\n7\nfoo\n8\n9'|货物运输
福
福
然而,像这样在迭代器中返回周围的行似乎是不可能的,因为在循环的每次迭代中,前面的行不再是可访问的
我看到了这个类型,但它只在一个切片上起作用
这个板条箱似乎是我想要的那种功能,但我不知道如何让它在文件中的行上使用迭代器
我还查看了窗口迭代器函数,但没有看到任何类型的窗口迭代器函数
在我深入滚动我自己形式的line buffer对象以缓存以前看到的行(可能是某种环形缓冲区?)之前,我希望Rust中已经有一些方法可以轻松实现这一点 一种方法是开发一个状态机,它始终保持最后2N+1行的窗口。当找到匹配行时,它会将一个新条目添加到与未来行号关联的待打印待匹配项列表中。当达到该未来行号时,将打印具有该行号的条目,以及从上下文窗口中提取的上下文行,请记住,接近输入开头的匹配项的前导上下文行少于N行。当到达输入末尾时,将打印所有仍挂起的条目,请记住这些条目的尾随上下文行少于N行
祝你好运,与所有这些变异抗锈 有效地实现这一点是相当棘手的,而您使用滚动缓冲区的本能也非常重要。这就是GNUGREP和ripgrep所做的。如果您愿意产生一些依赖性,那么通过依赖ripgrep的一些内部库,您几乎可以实现您想要的。例如,这里有一个利用板条箱做你想做的事情的程序: 其产出是:
$printf'1\n2\n3\nfoo\n4\n5\n6\n7\nfoo\n8\n9'./target/release/grepex
3.
福
4.
--
7.
福
8.
如果您不需要正则表达式,则可以删除grep regex
依赖项,但它需要编写更多的代码来提供您自己的实现(如果您只需要简单的子字符串搜索,这比看起来更容易)
如果您确实想自己实现这一点,那么您可以尝试阅读。实际上,这一切都建立在一个
如果性能不太重要,则可以逐行循环,并保留足够的缓冲区来存储以前的
N
行(其中N
是“之前”窗口的大小)。找到匹配项后,从缓冲区打印上一行N
。同时,在N
处启动一个计数器,该计数器每连续一行减少1
。当计数器高于0
时,打印与“after”窗口相对应的行。(这与使用滚动缓冲区没有什么不同。)感谢您的链接,但“在任意迭代器/集合上使用块和窗口的最佳方法是首先将其收集到Vec中,然后对其进行迭代。”->这正是我试图避免的。我试图在Rust中复制grep
的确切功能,因此必须将整个文件读取到内存中,这违背了程序的目的。我一次只想在内存中保留n
+1行;正在迭代的行及其在输入流中的上一行和下一行。继续阅读答案中包含完整的、准备就绪的代码段的部分,该代码段创建了您想要的分块内容。“如果接受动态分配,那么就可以使用Vec
作为分块迭代器的项。”谢谢,我在那里得到了一个简单的答案,并在匹配后输出该行。它甚至具有与GNUgrep
完全相同的内存占用空间,在3GB文件上迭代时约为900KB,尽管需要4倍的时间(4分钟vs 1分钟)。但是,我不知道如何将其扩展到包括匹配之前的行,可能仍然需要某种缓冲区。是的,这大概就是我为自己的实现所做的工作,基本上是一个Vec
,它得到push
,然后在每次迭代中将每一行截断为n
大小,当找到匹配项时,打印Vec内容。这将适用于比赛前的线路。不过,我还没有弄明白比赛结束后该如何处理台词。我想这可能会对比赛后的台词有所帮助,但我找不到一个有效的例子来说明如何使用它
use std::error::Error;
use std::io;
use grep_regex::RegexMatcher;
use grep_searcher::{Searcher, SearcherBuilder, Sink, SinkContext, SinkMatch};
fn main() -> Result<(), Box<dyn Error>> {
let re = RegexMatcher::new(r"foo")?;
let mut searcher = SearcherBuilder::new()
.before_context(1)
.after_context(1)
.build();
searcher.search_reader(
&re,
io::stdin().lock(),
MySink(io::stdout().lock()),
)?;
Ok(())
}
struct MySink<W>(W);
impl<W: io::Write> Sink for MySink<W> {
type Error = io::Error;
fn matched(
&mut self,
_: &Searcher,
mat: &SinkMatch,
) -> Result<bool, io::Error> {
self.0.write_all(mat.bytes())?;
Ok(true)
}
fn context(
&mut self,
_: &Searcher,
ctx: &SinkContext,
) -> Result<bool, io::Error> {
self.0.write_all(ctx.bytes())?;
Ok(true)
}
fn context_break(
&mut self,
_: &Searcher,
) -> Result<bool, io::Error> {
self.0.write_all(b"--\n")?;
Ok(true)
}
}
[dependencies]
grep-regex = "0.1.5"
grep-searcher = "0.1.6"