在Rust 1.x中读取和写入文件的实际方法是什么?


136

随着Rust相对较新,我已经看到了太多的读写文件的方法。有人为他们的博客想出了很多非常混乱的片段,而我发现的示例(甚至在Stack Overflow中)有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() {
  |>                      ^^^^^

综上所述,我正在寻找的是:

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

您想如何读取文件?如所示,您要逐行显示吗?您是否想要一串全部?“读取文件”有多种方法。
Shepmaster 2015年

两种方式都可以。我故意将其打开。如果将它们全部收集到一个字符串中,则将其拆分为Vec <String>将是微不足道的,反之亦然。在寻找解决方案的这一点上,我很高兴能看到优雅,最新的Rust文件I / O代码有效。
贾里德(Jared)

3
关于特征错误(std::io::Read),请注意,在Rust中,您必须导入要明确使用的特征;因此,在这里您会丢失一个use std::io::Read(可能是use std::io::{Read,BufReader}将两个用途合并在一起的)
Matthieu M.

Answers:


197

我在这里展示的所有功能都没有单独引起恐慌,但是我正在使用,expect因为我不知道哪种错误处理最适合您的应用程序。去阅读锈病程序设计语言出错处理一章,以了解如何适当地自己的程序处理失败。

Rust 1.26及更高版本

如果您不想关心基本细节,则可以使用单行功能进行读写。

读取文件到 String

use std::fs;

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

读取文件为 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及更高版本

这些形式比为您分配String或的单行函数稍微冗长一些Vec,但是功能更强大,因为您可以重复使用分配的数据或追加到现有对象。

读取数据

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

读取文件到 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);
}

读取文件为 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());
}

写一个文件

写入文件是类似的,除了我们使用Writetrait并总是写出字节。您可以使用将String/ 转换&str为字节as_bytes

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当您想逐行阅读时,A 更为有用:

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);
    }
}

2
我实际上并没有太多的依据,但是在研究这一点时,我感到社区在推动使用BufReader和BufWriter而不是直接从文件读取到字符串方面有所推动。您对这些对象了解多少,或者您对答案中显示的“更经典”版本使用它们的利弊了解很多?
贾里德(Jared)

@TheDaleks我没有关注您的问题。b"foobar"是用于创建对字节数组(&[u8; N])的引用的文字。因此,它是不可变的。它没有给您带来您无法以简单方式进行的任何操作。
Shepmaster

@Shepmaster有时,使用字节数组而不是编码字符串是有利的。例如,如果要制作一个将文件从一个位置移动到另一个位置的应用程序,则需要具有原始字节,以免破坏该应用程序处理的可执行文件。
The Daleks

@TheDaleks是的,这就是为什么此答案解释了如何使用a Vec<u8>进行读写的原因。这些是原始字节。
Shepmaster
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.