首页 > 解决方案 > 使用 to_string 是按行读取文件并按空格分割的最干净的方法吗?

问题描述

我正在尝试读取格式如下的文件:

ruby 2.6.2
elixir 1.8.3

并像这样的伪代码转换成二维数组:

[
  ["ruby", "2.6.2"]
  ["elixir", "1.8.3"]
]

我必须在 Rust 中执行此操作的代码是:

use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};

use std::path::Path;

pub fn present() -> bool {
    path().exists()
}

pub fn parse() -> Vec<Vec<String>> {
    let f = BufReader::new(file().unwrap());
    f.lines()
        .map(|line| {
            line.unwrap()
                .split_whitespace()
                .map(|x| x.to_string())
                .collect()
        })
        .collect()
}

fn file() -> io::Result<File> {
    let f = File::open(path())?;
    return Ok(f);
}

fn path<'a>() -> &'a Path {
    return Path::new(".tool-versions");
}

我在这里不确定的是parse函数中间的这一行:

.map(|x| x.to_string())

这似乎有点“过度劳累”,但我不确定我的感觉是否正确。

我在这里遗漏了什么,或者这是编写此代码以完成此特定任务的最干净的方法?

标签: rust

解决方案


您的代码通常是惯用的并且很好,我只是有一些小警告。

至于“过度工作”,我认为就地修改字符串是过度工作,因为在每个删除的空白字符处,您都需要做以下三件事之一:

  1. 将超过该元素的每个字符向下移动 1(大多数移动,但不需要分配)。
  2. 存储每个位置的索引,以相反的顺序迭代并将每个元素向下移动 1(更少移动,但需要分配)。
  3. 存储每个位置的索引,跟踪起始当前空白块的索引,当前空白块的结束和下一个块的开始,并向下移动每个元素,然后跟踪这些移位(最复杂,需要最少的移动,并且比需要的计算成本更高)。

或者..你可以分配一个新的字符串。有时简单是强大的。

对于其余部分,一个主要问题是不要不必要地恐慌,尤其是不要因为无法打开文件而感到恐慌。展开,除非你能证明它们不应该发生,否则不适合生产代码。具体来说,以下行可能会引起恐慌。

let f = BufReader::new(file().unwrap());

最好将该功能替换为:

pub fn parse() -> io::Result<Vec<Vec<String>>> {
    let f = BufReader::new(file()?);
    f.lines()
        .map(|line| {
            line.map(|x| {
                x
                    .split_whitespace()
                    .map(|x| x.to_string())
                    .collect()
            })
        })
        .collect()
}

这样,调用者parse可以适当地处理任何错误,无论是在创建 BufReader 期间还是在lines().


推荐阅读