Cách đọc và ghi tập tin thực tế trong Rust 1.x là gì?


136

Với việc Rust tương đối mới, tôi đã thấy quá nhiều cách đọc và ghi tệp. Nhiều đoạn cực kỳ lộn xộn mà ai đó đã đưa ra cho blog của họ và 99% các ví dụ tôi đã tìm thấy (ngay cả trên Stack Overflow) là từ các bản dựng không ổn định không còn hoạt động. Bây giờ Rust đã ổn định, một đoạn mã đơn giản, dễ đọc, không hoảng loạn để đọc hoặc ghi tệp là gì?

Đây là lần gần nhất tôi có được thứ gì đó hoạt động trong việc đọc tệp văn bản, nhưng nó vẫn không biên dịch mặc dù tôi khá chắc chắn rằng tôi đã bao gồm mọi thứ tôi nên có. Điều này dựa trên đoạn trích tôi tìm thấy trên Google+ của tất cả các địa điểm và điều duy nhất tôi đã thay đổi là cái cũ BufferedReaderbây giờ chỉ là 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);
    }
}

Trình biên dịch phàn nàn:

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() {
  |>                      ^^^^^

Tóm lại, điều tôi đang tìm kiếm là:

  • ngắn gọn
  • khả năng đọc
  • bao gồm tất cả các lỗi có thể
  • không hoảng sợ

Bạn muốn đọc tập tin như thế nào? Bạn có muốn nó từng dòng một, như bạn đã thể hiện? Bạn có muốn tất cả trong một chuỗi? Có nhiều hơn một cách để "đọc một tập tin".
Người quản lý

Cả hai cách đều ổn. Tôi để nó mở cố ý. Nếu nó được thu thập tất cả thành một chuỗi, thì việc tách nó thành Vec <String> sẽ không đáng kể và ngược lại. Tại thời điểm này trong quá trình tìm kiếm giải pháp của mình, tôi sẽ rất vui khi thấy mã I / O của tệp Rust thanh lịch, cập nhật hoạt động.
Jared

3
Liên quan đến lỗi đặc điểm ( std::io::Read), lưu ý rằng trong Rust, bạn phải nhập các đặc điểm bạn muốn sử dụng một cách rõ ràng ; do đó, ở đây bạn đang thiếu một use std::io::Read(có thể là use std::io::{Read,BufReader}để kết hợp hai cách sử dụng lại với nhau)
Matthieu M.

Câu trả lời:


197

Không có chức năng nào tôi hiển thị ở đây hoảng loạn, nhưng tôi đang sử dụng expectvì tôi không biết cách xử lý lỗi nào sẽ phù hợp nhất với ứng dụng của bạn. Go đọc Các Rust Lập trình Ngôn ngữ 's chương về xử lý lỗi để hiểu làm thế nào để xử lý một cách thích hợp thất bại trong chương trình của riêng bạn.

Rust 1.26 trở đi

Nếu bạn không muốn quan tâm đến các chi tiết cơ bản, có các hàm một dòng để đọc và viết.

Đọc một tập tin cho một String

use std::fs;

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

Đọc một tập tin như là một Vec<u8>

use std::fs;

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

Viết một tập tin

use std::fs;

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

Rust 1.0 trở đi

Các biểu mẫu này dài hơn một chút so với các hàm một dòng phân bổ một Stringhoặc Veccho bạn, nhưng mạnh hơn ở chỗ bạn có thể sử dụng lại dữ liệu được phân bổ hoặc nối vào một đối tượng hiện có.

Đọc dữ liệu

Đọc một tập tin đòi hỏi hai phần cốt lõi: FileRead.

Đọc một tập tin cho một 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);
}

Đọc một tập tin như là một 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());
}

Viết một tập tin

Viết một tệp tương tự, ngoại trừ chúng tôi sử dụng Writeđặc điểm và chúng tôi luôn viết ra các byte. Bạn có thể chuyển đổi a String/ &strthành byte bằng 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");
}

Bộ đệm I / O

Tôi cảm thấy một chút thúc đẩy từ cộng đồng để sử dụng BufReaderBufWriterthay vì đọc thẳng từ một tập tin

Một trình đọc đệm (hoặc nhà văn) sử dụng bộ đệm để giảm số lượng yêu cầu I / O. Ví dụ, truy cập đĩa một lần sẽ hiệu quả hơn nhiều khi đọc 256 byte thay vì truy cập đĩa 256 lần.

Điều đó đang được nói, tôi không tin rằng một trình đọc / ghi đệm được đệm sẽ hữu ích khi đọc toàn bộ tệp. read_to_enddường như sao chép dữ liệu trong các phần lớn, do đó việc chuyển có thể được kết hợp một cách tự nhiên thành ít yêu cầu I / O hơn.

Đây là một ví dụ về việc sử dụng nó để đọc:

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

Và để viết:

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

A BufReaderhữu ích hơn khi bạn muốn đọc từng dòng một:

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
Tôi thực sự không có nhiều cơ sở để làm điều này, nhưng trong khi nghiên cứu điều này, tôi cảm thấy một chút thúc đẩy từ cộng đồng để sử dụng BufReader và BufWriter thay vì đọc thẳng từ tệp sang chuỗi. Bạn có biết nhiều về những đối tượng này hay những ưu và nhược điểm của việc sử dụng chúng qua phiên bản "cổ điển hơn" mà bạn đã thể hiện trong câu trả lời của mình không?
Jared

@TheDaleks Tôi không theo dõi câu hỏi của bạn. b"foobar"là một nghĩa đen để tạo một tham chiếu đến một mảng byte ( &[u8; N]). Như vậy, nó là bất biến. Không có gì nó mang lại cho bạn mà bạn không thể làm theo cách đơn giản hơn.
Người quản lý

@Shepmaster Đôi khi có một mảng byte thay vì chuỗi được mã hóa; ví dụ: nếu bạn muốn tạo một ứng dụng di chuyển các tệp từ vị trí này sang vị trí khác, bạn cần phải có các byte thô để bạn không làm hỏng các tệp thực thi mà ứng dụng xử lý.
The Daleks

@TheDaleks có, đó là lý do tại sao câu trả lời này giải thích cách sử dụng Vec<u8>để đọc và viết. Đó là những byte thô.
Người quản lý
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.