วิธีการอ่านและเขียนไฟล์ใน Rust 1.x โดยพฤตินัยคืออะไร?


148

เนื่องจาก 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);
    }
}

คอมไพเลอร์บ่นว่า:

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

ลักษณะอย่างใดอย่างหนึ่งก็ดี ฉันเปิดทิ้งไว้โดยเจตนา หากรวบรวมทั้งหมดเป็นสตริงเดียวการแยกออกเป็น Vec <String> จะเป็นเรื่องเล็กน้อยและในทางกลับกัน ณ จุดนี้ในการค้นหาวิธีแก้ไขฉันยินดีที่จะเห็นรหัส I / O ของไฟล์ Rust ที่สวยงามและทันสมัยซึ่งใช้งานได้จริง
Jared

3
เกี่ยวกับข้อผิดพลาดลักษณะ ( std::io::Read) ทราบว่าใน Rust คุณต้องนำเข้าลักษณะที่คุณคาดว่าจะใช้อย่างชัดเจน ; ดังนั้นที่นี่คุณจะขาดuse std::io::Read(ซึ่งอาจเป็นการuse std::io::{Read,BufReader}รวมการใช้ทั้งสองเข้าด้วยกัน)
Matthieu M.

คำตอบ:


211

ไม่มีฟังก์ชั่นใดที่ฉันแสดงที่นี่ให้ตื่นตระหนกด้วยตัวเอง แต่ฉันกำลังใช้expectเพราะฉันไม่รู้ว่าการจัดการข้อผิดพลาดแบบใดจะเหมาะกับแอปพลิเคชันของคุณมากที่สุด ไปอ่านสนิมโปรแกรมภาษาของบทที่เกี่ยวกับการจัดการข้อผิดพลาดจะเข้าใจวิธีการที่เหมาะสมจัดการกับความล้มเหลวในโปรแกรมของคุณเอง

สนิม 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ให้คุณเล็กน้อย แต่มีประสิทธิภาพมากกว่าที่คุณสามารถใช้ข้อมูลที่จัดสรรซ้ำหรือผนวกเข้ากับอ็อบเจ็กต์ที่มีอยู่ได้

การอ่านข้อมูล

การอ่านไฟล์ต้องใช้สองส่วนหลัก: FileและRead.

อ่านไฟล์เป็นไฟล์ 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());
}

เขียนไฟล์

การเขียนไฟล์จะคล้ายกันยกเว้นว่าเราใช้Writeลักษณะและเขียนเป็นไบต์เสมอ คุณสามารถแปลง a 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

ฉันรู้สึกถึงแรงผลักดันจากชุมชนให้ใช้BufReaderและBufWriterแทนที่จะอ่านจากไฟล์โดยตรง

ตัวอ่านบัฟเฟอร์ (หรือตัวเขียน) ใช้บัฟเฟอร์เพื่อลดจำนวนคำขอ 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");
}

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

2
ฉันไม่ได้มีพื้นฐานจากสิ่งนี้มากนัก แต่ในขณะที่ค้นคว้าสิ่งนี้ฉันรู้สึกได้ถึงแรงผลักดันจากชุมชนให้ใช้ BufReader และ BufWriter แทนที่จะอ่านจากไฟล์ไปยังสตริงโดยตรง คุณรู้มากเกี่ยวกับวัตถุเหล่านี้หรือข้อดีข้อเสียของการใช้วัตถุเหล่านี้ในเวอร์ชัน "คลาสสิกกว่า" ที่คุณได้แสดงไว้ในคำตอบของคุณหรือไม่
Jared

@TheDaleks ฉันไม่ได้ติดตามคำถามของคุณ b"foobar"เป็นลิเทอรัลเพื่อสร้างการอ้างอิงอาร์เรย์ของไบต์ ( &[u8; N]) ดังนั้นจึงไม่เปลี่ยนรูป ไม่มีอะไรให้คุณทำไม่ได้ด้วยวิธีที่ง่ายกว่านี้
Shepmaster

@Shepmaster ในบางครั้งมันเป็นประโยชน์ที่จะมีไบต์อาร์เรย์แทนที่จะเป็นสตริงที่เข้ารหัส ตัวอย่างเช่นหากคุณต้องการสร้างแอปที่ย้ายไฟล์จากจุดหนึ่งไปยังอีกจุดหนึ่งคุณต้องมีไบต์ดิบเพื่อไม่ให้ไฟล์ปฏิบัติการที่แอปประมวลผลเสียหาย
The Daleks

@ TheDaleks ใช่นั่นคือเหตุผลที่คำตอบนี้อธิบายถึงวิธีการใช้ a Vec<u8>สำหรับการอ่านและการเขียน เป็นไบต์ดิบ
Shepmaster
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.