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

Jar*_*red 107 file-io rust

由于Rust比较新,我看到了很多阅读和编写文件的方法.许多人为他们的博客提出了非常混乱的片段,我发现的99%的例子(即使在Stack Overflow上)来自不稳定的构建,不再有效.现在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨:

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() {
  |>                      ^^^^^
Run Code Online (Sandbox Code Playgroud)

总结一下,我正在寻找的是:

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

She*_*ter 156

我在这里展示的所有功能都没有自己恐慌,但我正在使用,expect因为我不知道哪种错误处理最适合您的应用程序.阅读Rust编程语言关于错误处理章节,了解如何在您自己的程序中正确处理失败.

Rust 1.26及以后

如果您不想关心底层细节,可以使用单行函数进行读写.

将文件读取到 String

use std::fs;

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

读取文件为 Vec<u8>

use std::fs;

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

写一个文件

use std::fs;

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

Rust 1.0及以后

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

读数据

阅读文件需要两个核心部分: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);
}
Run Code Online (Sandbox Code Playgroud)

读取文件为 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());
}
Run Code Online (Sandbox Code Playgroud)

写一个文件

编写文件是类似的,除了我们使用Write特征,我们总是写出字节.您可以使用以下命令将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");
}
Run Code Online (Sandbox Code Playgroud)

缓冲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);
}
Run Code Online (Sandbox Code Playgroud)

并写作:

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");
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • bufwriter 是否需要随时显式刷新? (3认同)
  • 我真的没有太多的依据,但是在研究这个时,我觉得社区有点推动使用 BufReader 和 BufWriter 而不是直接从文件读取到字符串。您是否对这些对象或使用它们的优缺点了解很多,而不是您在答案中显示的“更经典”版本? (2认同)
  • @Dragoon 这真的取决于你的用例。[文档说](https://doc.rust-lang.org/std/io/struct.BufWriter.html): *在删除 `BufWriter&lt;W&gt;` 之前调用 `flush` 至关重要。尽管丢弃将尝试刷新缓冲区的内容,但丢弃过程中发生的任何错误都将被忽略。调用“flush”可确保缓冲区为空,因此删除甚至不会尝试文件操作。* (2认同)

Dra*_*oon 28

对于任何写入文件的人来说,接受的答案很好,但如果您需要附加到文件,则必须使用 OpenOptions 结构:

use std::io::Write;
use std::fs::OpenOptions;

fn main() {
    let data = "Some data!\n";
    let mut f = OpenOptions::new()
        .append(true)
        .create(true) // Optionally create the file if it doesn't already exist
        .open("/tmp/foo")
        .expect("Unable to open file");
    f.write_all(data.as_bytes()).expect("Unable to write data");
}
Run Code Online (Sandbox Code Playgroud)

缓冲写入仍然以相同的方式工作:

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

fn main() {
    let data = "Some data!\n";
    let f = OpenOptions::new()
        .append(true)
        .open("/tmp/foo")
        .expect("Unable to open file");
    let mut f = BufWriter::new(f);
    f.write_all(data.as_bytes()).expect("Unable to write data");
}
Run Code Online (Sandbox Code Playgroud)

  • 我发现我还需要为上层流程导入 `std::io::Write` 。 (2认同)

小智 11

通过使用Buffered I/O可以复制大于实际内存大小的文件。


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

fn main() {
    let read = File::open(r#"E:\1.xls"#);

    let write = OpenOptions::new().write(true).create(true).open(r#"E:\2.xls"#);

    let mut reader = BufReader::new(read.unwrap());

    let mut writer = BufWriter::new(write.unwrap());

    let mut length = 1;

    while length > 0 {
        let buffer = reader.fill_buf().unwrap();

        writer.write(buffer);

        length = buffer.len();
        reader.consume(length);
    }
}
Run Code Online (Sandbox Code Playgroud)