Rust 1.x에서 파일을 읽고 쓰는 실질적인 방법은 무엇입니까?


136

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

요약하면, 내가 찾고있는 것은 :

  • 짧음
  • 가독성
  • 가능한 모든 오류를 다룹니다
  • 당황하지 않습니다

파일을 어떻게 읽고 싶습니까? 표시 한대로 한 줄씩 표시 하시겠습니까? 하나의 문자열로 모두 원하십니까? "파일을 읽는"방법은 여러 가지가 있습니다.
Shepmaster

어느 쪽이든 괜찮습니다. 의도적으로 열어 두었습니다. 모두 하나의 문자열로 수집되면 Vec <String>으로 분할하는 것이 쉽지 않으며 그 반대도 마찬가지입니다. 솔루션 검색 에서이 시점에서, 우아하고 최신의 Rust 파일 I / O 코드가 작동하는 것을 보게되어 기쁩니다.
Jared

3
특성 오류 ( std::io::Read) 와 관련하여 Rust에서는 명시 적 으로 사용할 것으로 예상되는 특성을 가져와야합니다 . 그리하여 여기에 use std::io::Read( use std::io::{Read,BufReader}두 용도를 합치기위한 a 일 수 있음 )
Matthieu M.

답변:


197

여기에 표시된 기능 중 어느 것도 독자적으로 패닉 상태에 빠지지 않지만 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");
}

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

파일 작성

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

버퍼링 된 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");
}

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 간혹 인코딩 된 문자열 대신 바이트 배열을 사용하는 것이 유리합니다. 예를 들어, 한 지점에서 다른 지점으로 파일을 이동하는 앱을 만들려면 앱에서 처리하는 실행 파일이 손상되지 않도록 원시 바이트가 있어야합니다.
Daleks

@ TheDaleks 예,이 답변 Vec<u8>은 읽기 및 쓰기 에 사용하는 방법을 설명하는 이유 입니다. 그것들은 원시 바이트입니다.
Shepmaster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.