Ponieważ Rust jest stosunkowo nowy, widziałem zbyt wiele sposobów czytania i zapisywania plików. Wiele z nich to wyjątkowo niechlujne fragmenty, które ktoś wymyślił na swoim blogu, a 99% przykładów, które znalazłem (nawet w przypadku przepełnienia stosu) pochodzi z niestabilnych kompilacji, które już nie działają. Teraz, gdy Rust jest stabilny, jaki jest prosty, czytelny, niepanikujący fragment do odczytu lub zapisu plików?
Jest to najbliższe osiągnięcie czegoś, co działa pod względem odczytu pliku tekstowego, ale nadal nie kompiluje się, mimo że jestem prawie pewien, że uwzględniłem wszystko, co powinienem. Jest to oparte na fragmencie, który znalazłem w Google+ wszystkich miejsc, a jedyne, co zmieniłem, to to, że stary BufferedReader
jest teraz po prostu 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);
}
}
Kompilator narzeka:
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() {
|> ^^^^^
Podsumowując, szukam:
- zwięzłość
- czytelność
- obejmuje wszystkie możliwe błędy
- nie panikuje
std::io::Read
), zwróć uwagę, że w Rust musisz zaimportować cechy, których zamierzasz używać jawnie ; więc tutaj brakujeuse std::io::Read
(które mogłoby byćuse std::io::{Read,BufReader}
połączeniem dwóch zastosowań razem)Odpowiedzi:
Żadna z funkcji, które tu pokazuję, sama nie panikuje, ale używam,
expect
ponieważ nie wiem, jaki rodzaj obsługi błędów najlepiej pasuje do Twojej aplikacji. Go przeczytać rdza język programowania jest rozdział dotyczący obsługi błędów , aby zrozumieć, jak prawidłowo obsługiwać awarię w swoim własnym programie.Rust 1.26 i nowsze
Jeśli nie chcesz przejmować się podstawowymi szczegółami, dostępne są jednowierszowe funkcje do czytania i pisania.
Przeczytaj plik do pliku
String
use std::fs; fn main() { let data = fs::read_to_string("/etc/hosts").expect("Unable to read file"); println!("{}", data); }
Przeczytaj plik jako plik
Vec<u8>
use std::fs; fn main() { let data = fs::read("/etc/hosts").expect("Unable to read file"); println!("{}", data.len()); }
Napisz plik
use std::fs; fn main() { let data = "Some data!"; fs::write("/tmp/foo", data).expect("Unable to write file"); }
Rust 1.0 i nowsze
Formularze te są nieco bardziej szczegółowe niż funkcje jednowierszowe, które przydzielają
String
lubVec
dla Ciebie, ale mają większe możliwości, ponieważ można ponownie wykorzystać przydzielone dane lub dołączyć je do istniejącego obiektu.Czytanie danych
Odczyt pliku wymaga dwóch podstawowych elementów:
File
iRead
.Przeczytaj plik do pliku
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); }
Przeczytaj plik jako plik
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()); }
Napisz plik
Pisanie pliku jest podobne, z tym że używamy
Write
cechy i zawsze wypisujemy bajty. Możesz przekonwertowaćString
/&str
na bajty za pomocą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"); }
Buforowane we / wy
Buforowany czytnik (lub program zapisujący) używa bufora w celu zmniejszenia liczby żądań we / wy. Na przykład o wiele bardziej efektywne jest jednorazowe uzyskanie dostępu do dysku w celu odczytania 256 bajtów zamiast dostępu do dysku 256 razy.
Biorąc to pod uwagę, nie sądzę, aby buforowany czytnik / pisarz był przydatny podczas czytania całego pliku.
read_to_end
wydaje się, że kopiuje dane w dość dużych porcjach, więc transfer może być już naturalnie połączony w mniejszą liczbę żądań we / wy.Oto przykład użycia go do czytania:
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); }
A do pisania:
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
jest bardziej przydatny, gdy chcesz czytać wiersz po wierszu: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); } }
źródło
b"foobar"
jest literałem do tworzenia referencji do tablicy bajtów (&[u8; N]
). Jako taki jest niezmienny. Nie ma nic z tego, czego nie możesz zrobić w prostszy sposób.Vec<u8>
do czytania i pisania. To są surowe bajty.