Перебор строк в файле и поиск подстроки из vec! в rust

#string #rust #match

#строка #Ржавчина #совпадение

Вопрос:

Я пишу проект, в котором структура System может быть создана из файла данных. В файле данных некоторые строки содержат ключевые слова, которые указывают значения, которые должны быть прочитаны либо внутри строки, либо в последующих N следующих строках (отделенных от строки пустой строкой).

Я хотел бы иметь a 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<amp;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<amp;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 мне нужно разворачивать дважды, как в let atline = lines.next().unwrap().unwrap(); ? Я думаю, что компилятор пока не жалуется из-за первой ошибки, с которой он сталкивается, которая
  2. Я не могу понять, какой тип присваивается значению k, поскольку я получаю типичный:
 error[E0308]: mismatched types
 --> src/system/system.rs:65:39
  |
65 |                     if split.contains(k) {
  |                                       ^ expected `amp;str`, found `str`
  |
  = note: expected reference `amp;amp;str`
             found reference `amp;str`

error: aborting due to previous error
 

Как мы должны объявить подстроку и сравнить ее со строками, которые я ввел keywd ? Я пытался использовать k в contains, сказать ему посмотреть amp;keywd и т. Д., Но я просто чувствую, что трачу свое время на неправильное решение проблемы. Заранее спасибо, любая помощь действительно приветствуется.

Комментарии:

1. Быстрый ответ на «ожидаемый amp;amp;str , найден amp;str » заключается в использовании amp;k , но вы правы в том, что исправление, которое проходит проверку типа, но затем приходит средство проверки заимствования и обнаруживает больше ошибок

Ответ №1:

Давайте рассмотрим эти вопросы один за другим. Я пройдусь по тому, как они отображаются в коде.

Сначала вам нужно позаимствовать keywd в for цикле, т.е. amp;keywd . Потому что в противном keywd случае выполняется перемещение после первой итерации while цикла, и, следовательно, почему компилятор жалуется на это.

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

Далее, когда вы вызываете .unwrap() on 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 для каждого keywd , что не нужно. Таким образом, вы также можете переместить это за пределы своего for цикла.

В целом, это выглядит так:

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

Учитывая, что мы заимствовали amp;keywd , нам не нужно менять k на amp;k , как сейчас k уже amp;amp;str .

Комментарии:

1. Я думаю, это сделало свое дело. В последующем сопоставлении мне все равно пришлось разыменовывать *k , чтобы сравнить его str , но пока, похоже, он запускается и делает то, что я хочу, в тестовом файле. Спасибо! Я люблю Rust за большую часть его дизайна, но я действительно не могу преодолеть проблемы с типом strings….

2. Ах, да, я пропустил совпадение. Rust действительно может быть немного сложным, просто подождите, и достаточно скоро вы будете использовать AsRef<str> and Cow<str> .