问题描述
首先我要说我是 Rust 的新手。实际上,这是我尝试编写的第一个 Rust 程序。
我能够逐行读取(大)文件并使用以下代码检查哪些行包含模式 "PerfectSwitch-0 : Message:"
:
use std::fs::File;
use std::io::{self,prelude::*,BufReader};
fn main() -> io::Result<()>{
let file = File::open("../test.out")?;
let reader = BufReader::new(file);
for line in reader.lines(){
let line = line.unwrap();
if line.contains("PerfectSwitch-0: Message:"){
println!("{}",line);
}
}
Ok(())
}
但是,我真正想做的是修改此代码,使我的模式可以匹配 "PerfectSwitch-0 : Message:"
、"PerfectSwitch-1 : Message:"
、"PerfectSwitch-2 : Message:"
、...、{{1} } 和 "PerfectSwitch-8 : Message:"
,没有正则表达式。
这样做的原因是我认为在这种情况下使用正则表达式有点矫枉过正,它可能会减慢我的程序(?)。
我尝试过编写 "PerfectSwitch-9 : Message:"
,但不出所料,它不起作用。
有人知道这是否可能吗?
谢谢
解决方法
在这种情况下,我会尝试使用 regex
,然后先看看它是否满足您的性能要求。
我认为一个好处是当您重新访问代码时,regex
更容易解析和更改。
例如,给定以下要解析的输入:
let input = vec![
"PerfectSwitch-42 : Message:","PerfectSwitch- : Message:","Message :","PerfectSwitch-271828 : Message:","PerfectSwitch-314159 : Message:","PerfectSwitch-",];
我们可以做到以下几点:
use regex::Regex;
fn main() {
let re = Regex::new(r"^PerfectSwitch-[0-9]+ : Message:").unwrap();
let result = input
.iter()
.filter(|&s| re.is_match(&s))
.collect::<Vec<_>>();
}
或者写一个粗糙的手写解决方案:
fn contains_switch(s: &str) -> bool {
let mut cursor = 0;
// Return early if the string is not at least as long as:
// - The length of "PerfectSwitch-" (14)
// - One or more ASCII digit(s) (1..)
// - One ASCII whitespace (1)
// - The length of ": Message:" (10)
if s.len() < 26 {
return false;
}
// Match on and consume "PerfectSwitch-"
if &s[..14] != "PerfectSwitch-" {
return false;
}
cursor += 14;
// Match on and consume ASCII digits
let digits = s[cursor..].bytes().take_while(u8::is_ascii_digit).count();
if digits == 0 {
return false;
}
cursor += digits;
// Match on and consume ASCII whitespace
if &s[cursor..cursor + 1] != " " {
return false;
}
cursor += 1;
// Match on and consume ": Message:"
if s.len() < cursor + 10 {
return false;
}
&s[cursor..cursor + 10] == ": Message:"
}
fn main() {
let result = input
.iter()
.filter(|&s| contains_switch(s))
.collect::<Vec<_>>();
}
我敢打赌第一个不太可能包含错误。
在这两种情况下,这应该给你:
[
"PerfectSwitch-42 : Message:",]
基准
迭代超过 1,000,000 条随机生成的行,以 glassbench
为基准,我们得到以下结果:
┌─┬───────────────┬──────────────┬─────────────┐
│#│ task │total duration│mean duration│
├─┼───────────────┼──────────────┼─────────────┤
│1│re_is_match │ 2.641099049s│ 52.82198ms│
│2│contains_switch│ 1.999254015s│ 7.37732ms│
└─┴───────────────┴──────────────┴─────────────┘
根据上述结果,以及维护性和可读性的权衡,我真的会选择使用 regex
板条箱。
您可以遍历所有可能的值:
let line = line.unwrap();
for i in 0..=9 {
if line.contains(&format!("PerfectSwitch-{}: Message:",i)) {
println!("{}",line);
}
}
尽管您可能想重新考虑正则表达式不好的假设。 Rust 的 regex
库速度非常快,我怀疑您在此处获得的任何小的性能提升都不会超过滚动您自己的解析代码所带来的可维护性不足。