【问题标题】:Check if the lines of a file contains given pattern without regex in Rust检查文件的行是否包含在 Rust 中没有正则表达式的给定模式
【发布时间】:2021-06-22 14:26:54
【问题描述】:

我首先要说我是 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:"、...、"PerfectSwitch-8 : Message:""PerfectSwitch-9 : Message:"没有正则表达式。

这样做的原因是我认为在这种情况下使用正则表达式有点过头了,它可能会减慢我的程序(?)。

我试过写if line.contains("PerfectSwitch-?: Message:"),但不出所料,它没有用。

有人知道这是否可行吗?

谢谢

【问题讨论】:

  • 为什么正则表达式过大?为什么你认为它会减慢你的程序?也许它会加快速度!为什么不试一试呢?
  • 我不确定。正如我所说,这是我第一次接触 Rust。不使用正则表达式似乎“更简单”。但我肯定会试一试。如果可以在没有正则表达式的情况下做我想做的事,那么进行一些比较会很好。
  • 我认为在这种特定情况下使用正则表达式会更简单。标准库没有模式匹配工具。所以你要么需要自己动手(不简单),要么只实现自己的小解析器(可能很简单,但不像使用正则表达式那么简单)。
  • 答案解决了您的问题吗?

标签: string rust matching


【解决方案1】:

我会在这种情况下尝试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:",
    "PerfectSwitch-271828 : Message:",
    "PerfectSwitch-314159 : 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 crate。

【讨论】:

  • 一般来说避免使用正则表达式箱似乎不是一个好主意,除非您需要保持二进制文件很小。这是一个经过精心调整的板条箱,通常是最有效(和方便)的解决方案。
  • 另外,随机生成的数据可能是 regex crate 最糟糕的情况之一。 :-)
【解决方案2】:

您可以遍历所有可能的值:

let line = line.unwrap();
for i in 0..=9 {
    if line.contains(&format!("PerfectSwitch-{}: Message:", i)) {
        println!("{}", line);
    }
}

尽管您可能想重新考虑您认为正则表达式不好的假设。 Rust 的 regex 库非常快,我怀疑你在这里获得的任何小的性能提升都会超过滚动你自己的解析代码所带来的可维护性不足。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 2011-02-11
    • 2015-07-10
    • 1970-01-01
    相关资源
    最近更新 更多