首页 > 解决方案 > 遍历文件中的行并从 vec 中查找子字符串!生锈

问题描述

我正在编写一个System可以从数据文件构造结构的项目。在数据文件中,一些行包含关键字,这些关键字指示要在该行内或在随后的 N 行中读取的值(用该行中的空白行分隔)。

我想要一个vec!包含关键字(在编译时静态已知),检查迭代器返回的行是否包含关键字并执行适当的操作。

现在我的代码如下所示:

impl System {
    fn read_data<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> where P: AsRef<Path> {
        let file = File::open(filename)?;
        let f = BufReader::new(file);
        Ok(f.lines())
    }
    ...
    pub fn new_from_data<P>(dataname: P) -> System where P: AsRef<Path> {
        let keywd = vec!["atoms", "atom types".into(),
                         "Atoms".into()];
        let mut sys = System::new();
        if let Ok(mut lines) = System::read_data(dataname) {
            while let Some(line) = lines.next() {
                for k in keywd {
                    let split: Vec<&str> = line.unwrap().split(" ").collect();
                    if split.contains(k) {
                        match k {
                        "atoms" => sys.natoms = split[0].parse().unwrap(),
                        "atom types" => sys.ntypes = split[0].parse().unwrap(),
                        "Atoms" => {
                            lines.next();
                            // assumes fields are: atom-ID molecule-ID atom-type q x y z
                            for _ in 1..=sys.natoms {
                                let atline = lines.next().unwrap().unwrap();
                                let data: Vec<&str> = atline.split(" ").collect();
                                let atid: i32 = data[0].parse().unwrap();
                                let molid: i32 = data[1].parse().unwrap();
                                let atype: i32 = data[2].parse().unwrap();
                                let charge: f32 = data[3].parse().unwrap();
                                let x: f32 = data[4].parse().unwrap();
                                let y: f32 = data[5].parse().unwrap();
                                let z: f32 = data[6].parse().unwrap();
                                let at = Atom::new(atid, molid, atype, charge, x, y, z);
                                sys.atoms.push(at);
                            };
                        },
                        _ => (),
                        }
                    }
                }
            }
        }
        sys
    }
}

我非常不确定两点:

  1. 我不知道我是否以惯用的方式逐行读取文件,因为我修补了书中的一些示例和 Rust 示例。但是返回一个迭代器让我想知道何时以及如何解开结果。例如,当在 while 循环中调用迭代器时,我是否必须像 in 一样解包两次let atline = lines.next().unwrap().unwrap();?我认为编译器还没有抱怨,因为它遇到的第一个错误是
  2. 当我得到一个典型的值时,我无法理解赋予值 k 的类型:
error[E0308]: mismatched types
 --> src/system/system.rs:65:39
  |
65 |                     if split.contains(k) {
  |                                       ^ expected `&str`, found `str`
  |
  = note: expected reference `&&str`
             found reference `&str`

error: aborting due to previous error

我们应该如何声明子字符串并将其与我输入的字符串进行比较keywd?我试图在包含中尊重 k,告诉它查看 &keywd 等,但我只是觉得我在浪费时间没有正确解决问题。在此先感谢,确实感谢任何帮助。

标签: stringrustmatch

解决方案


让我们一一解决问题。我将通过它们出现在代码中的内容。

首先,您需要keywdfor循环中借用,即&keywd. 因为否则keywd会在循环的第一次迭代后移动while,因此编译器会抱怨这一点。

for k in &keywd {
    let split: Vec<&str> = line.unwrap().split(" ").collect();

接下来,当您调用.unwrap()line,这是同样的问题。这会导致内部Ok值移出Result. 相反,您可以这样做line.as_ref().unwrap(),然后您获得对内部Ok值的引用并且不使用line结果。

或者,您可以.filter_map(Result::ok)在您的lines, 上完全避免 ( .as_ref()) .unwrap()

您可以将其直接添加到read_data甚​​至简单地使用返回类型impl ...

fn read_data<P>(filename: P) -> io::Result<impl Iterator<Item = String>>
where
    P: AsRef<Path>,
{
    let file = File::open(filename)?;
    let f = BufReader::new(file);
    Ok(f.lines().filter_map(Result::ok))
}

请注意,您正在line为each 拆分keywd,这是不必要的。所以你也可以把它移到你的for循环之外。

总而言之,它最终看起来像这样:

if let Ok(mut lines) = read_data("test.txt") {
    while let Some(line) = lines.next() {
        let split: Vec<&str> = line.split(" ").collect();
        for k in &keywd {
            if split.contains(k) {
                ...

既然我们借了&keywd,那么我们不需要像现在一样更改k为。&kk&&str


推荐阅读