由于 rust 迹相对较新,我已经看到了太多读取和写入文件的方法.很多都是有人为他们的博客设计的非常混乱的代码片段,我发现的99%的例子(即使是在堆栈溢出的情况下)都来自不稳定的版本,不再工作.既然Rust是稳定的,那么什么是一个简单、可读、不惊慌的代码段,用于读取或写入文件?

这是我最接近阅读文本文件的方法,但它仍然没有编译,尽管我相当确定我已经包含了我应该包含的所有内容.这是基于我在Google+上找到的所有地方的一个片段,我唯一改变的是旧的BufferedReader现在只有BufReader:

use std::fs::File;
use std::io::BufReader;
use std::path::Path;

fn main() {
    let path = Path::new("./textfile");
    let mut file = BufReader::new(File::open(&path));
    for line in file.lines() {
        println!("{}", line);
    }
}

编译器抱怨:

error: the trait bound `std::result::Result<std::fs::File, std::io::Error>: std::io::Read` is not satisfied [--explain E0277]
 --> src/main.rs:7:20
  |>
7 |>     let mut file = BufReader::new(File::open(&path));
  |>                    ^^^^^^^^^^^^^^
note: required by `std::io::BufReader::new`

error: no method named `lines` found for type `std::io::BufReader<std::result::Result<std::fs::File, std::io::Error>>` in the current scope
 --> src/main.rs:8:22
  |>
8 |>     for line in file.lines() {
  |>                      ^^^^^

总而言之,我想要的是:

  • 简洁
  • 可读性
  • 涵盖所有可能的错误
  • 不要惊慌

推荐答案

我在这里展示的函数没有一个是独立的,但我使用expect,因为我不知道哪种错误处理方式最适合您的应用程序.阅读The Rust Programming Language's chapter on error handling,了解如何正确处理自己程序中的故障.

Rust 1.26 and onwards

如果你不想关心潜在的细节,有一行函数用于阅读和写作.

Read a file to a String

use std::fs;

fn main() {
    let data = fs::read_to_string("/etc/hosts").expect("Unable to read file");
    println!("{}", data);
}

Read a file as a Vec<u8>

use std::fs;

fn main() {
    let data = fs::read("/etc/hosts").expect("Unable to read file");
    println!("{}", data.len());
}

写一个文件

use std::fs;

fn main() {
    let data = "Some data!";
    fs::write("/tmp/foo", data).expect("Unable to write file");
}

Rust 1.0 and onwards

这些表单比为您分配StringVec的单行函数稍微详细一些,但更强大的是,您可以重用分配的数据或附加到现有对象.

读取数据

读取文件需要两个核心部分:FileRead.

Read a file to a String

use std::fs::File;
use std::io::Read;

fn main() {
    let mut data = String::new();
    let mut f = File::open("/etc/hosts").expect("Unable to open file");
    f.read_to_string(&mut data).expect("Unable to read string");
    println!("{}", data);
}

Read a file as a Vec<u8>

use std::fs::File;
use std::io::Read;

fn main() {
    let mut data = Vec::new();
    let mut f = File::open("/etc/hosts").expect("Unable to open file");
    f.read_to_end(&mut data).expect("Unable to read data");
    println!("{}", data.len());
}

写一个文件

写一个文件是类似的,除了我们使用Write字符,我们总是写字节.您可以使用as_bytesString/&str转换为字节:

use std::fs::File;
use std::io::Write;

fn main() {
    let data = "Some data!";
    let mut f = File::create("/tmp/foo").expect("Unable to create file");
    f.write_all(data.as_bytes()).expect("Unable to write data");
}

缓冲I/O

我觉得社区有点推动我使用BufReaderBufWriter,而不是直接从文件中阅读

缓冲读取器(或写入器)使用缓冲区来减少I/O请求的数量.例如,一次访问磁盘读取256字节要比访问磁盘256次有效得多.

话虽如此,我不相信缓冲读写器在读取整个文件时会有用.read_to_end似乎以较大的块复制数据,因此传输可能已经自然地合并为更少的I/O请求.

下面是一个使用它阅读的例子:

use std::fs::File;
use std::io::{BufReader, Read};

fn main() {
    let mut data = String::new();
    let f = File::open("/etc/hosts").expect("Unable to open file");
    let mut br = BufReader::new(f);
    br.read_to_string(&mut data).expect("Unable to read string");
    println!("{}", data);
}

至于写作:

use std::fs::File;
use std::io::{BufWriter, Write};

fn main() {
    let data = "Some data!";
    let f = File::create("/tmp/foo").expect("Unable to create file");
    let mut f = BufWriter::new(f);
    f.write_all(data.as_bytes()).expect("Unable to write data");
}

当你想逐行阅读时,BufReader更有用:

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = File::open("/etc/hosts").expect("Unable to open file");
    let f = BufReader::new(f);

    for line in f.lines() {
        let line = line.expect("Unable to read line");
        println!("Line: {}", line);
    }
}

Rust相关问答推荐

预期 struct VecDeque Student发现 struct VecDeque Student(Student)

通用池类型xsx

Rust kill std::processs::child

如何对字符串引用的引用向量进行排序,而不是对最外层的引用进行排序?

如果A == B,则将Rc A下推到Rc B

通过使用光标拖动角来绕其中心旋转矩形

关于如何初始化弱 struct 字段的语法问题

函数内模块的父作用域的访问类型

在我的Cargo 中,当我在建筑物中使用时,找不到我可以在产品包中使用的 crate .r我如何解决这个问题?

一种随机局部搜索算法的基准(分数)

为什么`AlternateScreen`在读取输入键时需要按Enter键?

为什么AsyncRead在Box上的实现有一个Unpin特征绑定?

为什么我可以使用 &mut (**ref) 创建两个实时 &mut 到同一个变量?

Rust 1.70 中未找到 Trait 实现

由特征键控的不同 struct 的集合

如何在 Emacs Elisp 中获得类似格式化的 LSP?

为什么传递 option.as_ref 的行为不同于使用匹配块并将内部映射到 ref 自己?

Rust 中函数的类型同义词

无法把握借来的价值不够长寿,请解释

Rustlings 切片原语