mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-08-14 14:26:56 +00:00
Add file I/O exercises (file_io1-3)
Adds three new exercises under exercises/24_file_io/, as proposed in rust-lang/rustlings#2233: - file_io1: read a whole file with fs::read_to_string and handle the Result. - file_io2: read/write a file efficiently with BufReader/BufWriter. - file_io3: build a path with PathBuf and inspect it with .metadata(). Includes matching solutions and a README describing the learning goal of each exercise.
This commit is contained in:
parent
734461f2fb
commit
316311e08c
30
exercises/24_file_io/README.md
Normal file
30
exercises/24_file_io/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# File IO
|
||||
|
||||
Rust provides several file I/O functions in the standard library. Buffered
|
||||
reads and writes provide better performance by reducing the number of
|
||||
underlying system calls.
|
||||
|
||||
## Learning goals
|
||||
|
||||
- **file_io1** — Read an entire file into memory with `fs::read_to_string`
|
||||
and handle the `Result` it returns instead of unwrapping blindly, so you
|
||||
get comfortable with the basic "open, read, handle the error" pattern
|
||||
that most file I/O in Rust builds on.
|
||||
- **file_io2** — Wrap a `File` in a `BufReader` to read it line by line and
|
||||
a `BufWriter` to write the transformed output, so you understand why
|
||||
buffering matters for I/O performance and how `BufRead::lines` lets you
|
||||
process input incrementally instead of loading it all at once.
|
||||
- **file_io3** — Build a path with `PathBuf`, then call `.metadata()` to
|
||||
inspect properties like creation time, size, and permissions, so you can
|
||||
work with the filesystem safely across platforms without hardcoding
|
||||
path separators.
|
||||
|
||||
## Further information
|
||||
|
||||
Here is the documentation for these functions in the standard library.
|
||||
|
||||
- [read_to_string](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
|
||||
- [BufReader](https://doc.rust-lang.org/std/io/struct.BufReader.html)
|
||||
- [BufWriter](https://doc.rust-lang.org/std/io/struct.BufWriter.html)
|
||||
- [Path](https://doc.rust-lang.org/stable/std/path/struct.Path.html)
|
||||
- [PathBuf](https://doc.rust-lang.org/std/path/struct.PathBuf.html)
|
||||
51
exercises/24_file_io/file_io1.rs
Normal file
51
exercises/24_file_io/file_io1.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
const TEST_FILE_NAME: &str = "SampleTextFile.txt";
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
create_required_files()?;
|
||||
|
||||
let read_str_result = fs::read_to_string(TEST_FILE_NAME);
|
||||
|
||||
match read_str_result {
|
||||
Ok(contents) => {
|
||||
// TODO : What would be the expected text ?
|
||||
assert_eq!(, contents);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("File read error. {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
file_cleanup()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_required_files() -> Result<(), std::io::Error> {
|
||||
let file_path = Path::new(TEST_FILE_NAME);
|
||||
|
||||
if !file_path.exists() {
|
||||
fs::write(file_path, "This is the file content.")?;
|
||||
} else {
|
||||
println!("File already exist.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_cleanup() -> Result<(), std::io::Error> {
|
||||
let file_path = Path::new(TEST_FILE_NAME);
|
||||
|
||||
if file_path.exists() {
|
||||
fs::remove_file(file_path).inspect(|_| {
|
||||
println!("Test file {} deleted.", TEST_FILE_NAME);
|
||||
})?;
|
||||
} else {
|
||||
println!(
|
||||
"No cleanup necessary since {} not exist.",
|
||||
file_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
75
exercises/24_file_io/file_io2.rs
Normal file
75
exercises/24_file_io/file_io2.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
use std::path::Path;
|
||||
|
||||
const TEST_INPUT_FILE_NAME: &str = "MultiLineTextFile.txt";
|
||||
const TEST_OUTPUT_FILE_NAME: &str = "MultiLineOutputFile.txt";
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
create_required_files()?;
|
||||
|
||||
let input_file = fs::File::open(TEST_INPUT_FILE_NAME).inspect_err(|err| {
|
||||
eprintln!("{} file open error {:?}", TEST_INPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
// TODO : How to create a new BufReader using input file
|
||||
let buffered_input_file =;
|
||||
|
||||
let output_file = fs::File::create(TEST_OUTPUT_FILE_NAME).inspect_err(|err| {
|
||||
eprintln!("{} file open error {:?}", TEST_OUTPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
let mut buffered_file_writer = BufWriter::new(output_file);
|
||||
|
||||
let mut line_number = 1;
|
||||
|
||||
for line in buffered_input_file.lines() {
|
||||
let line = line.inspect_err(|err| {
|
||||
eprintln!("{} line parse error {:?}", TEST_INPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
buffered_file_writer
|
||||
.write(format!("Line {} : {}\n", line_number, line).as_bytes())
|
||||
.inspect_err(|err| {
|
||||
eprintln!("{} line write error {:?}", TEST_INPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
line_number += 1;
|
||||
}
|
||||
|
||||
println!("{} : lines processed", line_number - 1);
|
||||
file_cleanup()
|
||||
}
|
||||
|
||||
fn create_required_files() -> Result<(), std::io::Error> {
|
||||
let file_path = Path::new(TEST_INPUT_FILE_NAME);
|
||||
|
||||
if !file_path.exists() {
|
||||
let text = "This is the first line of the text.
|
||||
This is the second line.
|
||||
And this is the third and the last line.";
|
||||
fs::write(file_path, text).inspect_err(|err| {
|
||||
eprintln!("Couldn't create the test file : {}", err);
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_cleanup() -> Result<(), std::io::Error> {
|
||||
let file_names = vec![TEST_INPUT_FILE_NAME, TEST_OUTPUT_FILE_NAME];
|
||||
|
||||
for file_name in file_names {
|
||||
let file_path = Path::new(file_name);
|
||||
|
||||
if file_path.exists() {
|
||||
fs::remove_file(file_path).inspect(|_| {
|
||||
println!("Test file {} removed", file_name);
|
||||
})?;
|
||||
} else {
|
||||
println!("No cleanup necessary since {} not exist.", file_name);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
78
exercises/24_file_io/file_io3.rs
Normal file
78
exercises/24_file_io/file_io3.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use std::fs;
|
||||
use std::io::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
create_required_files()?;
|
||||
let mut path_buffer = PathBuf::new();
|
||||
|
||||
path_buffer.push("SampleFilesFolder");
|
||||
path_buffer.push("MultiLineTextFile.txt");
|
||||
|
||||
// TODO : How to get metadata using path_buffer ?
|
||||
let meta_data_result = path_buffer.
|
||||
|
||||
match meta_data_result {
|
||||
Ok(meta_data) => {
|
||||
println!("Metadata about the file : {:?}", path_buffer);
|
||||
println!("File creation time {:?}", meta_data.created());
|
||||
println!("File size {}", meta_data.len());
|
||||
assert_eq!(meta_data.len(), 117);
|
||||
println!("File permissions {:?}", meta_data.permissions());
|
||||
assert!(!meta_data.permissions().readonly());
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("Could not get metadata. Error: {:?}", error);
|
||||
}
|
||||
}
|
||||
|
||||
file_cleanup()
|
||||
}
|
||||
|
||||
fn create_required_files() -> Result<(), std::io::Error> {
|
||||
let file_path = PathBuf::from("SampleFilesFolder/MultiLineTextFile.txt");
|
||||
|
||||
let dir_path = match file_path.parent(){
|
||||
Some(parent) => parent,
|
||||
None => return Err(Error::other("Could not get parent path")),
|
||||
};
|
||||
|
||||
if !dir_path.exists() {
|
||||
fs::create_dir(dir_path).inspect_err(|x| {
|
||||
eprintln!("Could not create directory: {:?}", x);
|
||||
})?;
|
||||
}
|
||||
|
||||
if !file_path.exists() {
|
||||
let text = "This is the first line of the text.
|
||||
This is the second line.
|
||||
And this is the third and the last line.";
|
||||
fs::write(&file_path, text).inspect_err(|err| {
|
||||
eprintln!("Couldn't create test file: {:?}", err);
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_cleanup() -> Result<(), std::io::Error> {
|
||||
let mut path_buffer = PathBuf::new();
|
||||
|
||||
path_buffer.push("SampleFilesFolder");
|
||||
path_buffer.push("MultiLineTextFile.txt");
|
||||
|
||||
if path_buffer.exists() {
|
||||
fs::remove_file(&path_buffer).inspect(|_| {
|
||||
println!("Test file removed");
|
||||
})?;
|
||||
}
|
||||
path_buffer.pop();
|
||||
|
||||
if path_buffer.exists() {
|
||||
fs::remove_dir(&path_buffer).inspect(|_| {
|
||||
println!("Test dir removed");
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
50
solutions/24_file_io/file_io1.rs
Normal file
50
solutions/24_file_io/file_io1.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
const TEST_FILE_NAME: &str = "SampleTextFile.txt";
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
create_required_files()?;
|
||||
|
||||
let read_str_result = fs::read_to_string(TEST_FILE_NAME);
|
||||
|
||||
match read_str_result {
|
||||
Ok(contents) => {
|
||||
assert_eq!("This is the file content.", contents);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("File read error. {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
file_cleanup()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_required_files() -> Result<(), std::io::Error> {
|
||||
let file_path = Path::new(TEST_FILE_NAME);
|
||||
|
||||
if !file_path.exists() {
|
||||
fs::write(file_path, "This is the file content.")?;
|
||||
} else {
|
||||
println!("File already exist.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_cleanup() -> Result<(), std::io::Error> {
|
||||
let file_path = Path::new(TEST_FILE_NAME);
|
||||
|
||||
if file_path.exists() {
|
||||
fs::remove_file(file_path).inspect(|_| {
|
||||
println!("Test file {} deleted.", TEST_FILE_NAME);
|
||||
})?;
|
||||
} else {
|
||||
println!(
|
||||
"No cleanup necessary since {} not exist.",
|
||||
file_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
74
solutions/24_file_io/file_io2.rs
Normal file
74
solutions/24_file_io/file_io2.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
use std::path::Path;
|
||||
|
||||
const TEST_INPUT_FILE_NAME: &str = "MultiLineTextFile.txt";
|
||||
const TEST_OUTPUT_FILE_NAME: &str = "MultiLineOutputFile.txt";
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
create_required_files()?;
|
||||
|
||||
let input_file = fs::File::open(TEST_INPUT_FILE_NAME).inspect_err(|err| {
|
||||
eprintln!("{} file open error {:?}", TEST_INPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
let buffered_input_file = BufReader::new(input_file);
|
||||
|
||||
let output_file = fs::File::create(TEST_OUTPUT_FILE_NAME).inspect_err(|err| {
|
||||
eprintln!("{} file open error {:?}", TEST_OUTPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
let mut buffered_file_writer = BufWriter::new(output_file);
|
||||
|
||||
let mut line_number = 1;
|
||||
|
||||
for line in buffered_input_file.lines() {
|
||||
let line = line.inspect_err(|err| {
|
||||
eprintln!("{} line parse error {:?}", TEST_INPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
buffered_file_writer
|
||||
.write(format!("Line {} : {}\n", line_number, line).as_bytes())
|
||||
.inspect_err(|err| {
|
||||
eprintln!("{} line write error {:?}", TEST_INPUT_FILE_NAME, err);
|
||||
})?;
|
||||
|
||||
line_number += 1;
|
||||
}
|
||||
|
||||
println!("{} : lines processed", line_number - 1);
|
||||
file_cleanup()
|
||||
}
|
||||
|
||||
fn create_required_files() -> Result<(), std::io::Error> {
|
||||
let file_path = Path::new(TEST_INPUT_FILE_NAME);
|
||||
|
||||
if !file_path.exists() {
|
||||
let text = "This is the first line of the text.
|
||||
This is the second line.
|
||||
And this is the third and the last line.";
|
||||
fs::write(file_path, text).inspect_err(|err| {
|
||||
eprintln!("Couldn't create the test file : {}", err);
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_cleanup() -> Result<(), std::io::Error> {
|
||||
let file_names = vec![TEST_INPUT_FILE_NAME, TEST_OUTPUT_FILE_NAME];
|
||||
|
||||
for file_name in file_names {
|
||||
let file_path = Path::new(file_name);
|
||||
|
||||
if file_path.exists() {
|
||||
fs::remove_file(file_path).inspect(|_| {
|
||||
println!("Test file {} removed", file_name);
|
||||
})?;
|
||||
} else {
|
||||
println!("No cleanup necessary since {} not exist.", file_name);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
77
solutions/24_file_io/file_io3.rs
Normal file
77
solutions/24_file_io/file_io3.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use std::fs;
|
||||
use std::io::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
create_required_files()?;
|
||||
let mut path_buffer = PathBuf::new();
|
||||
|
||||
path_buffer.push("SampleFilesFolder");
|
||||
path_buffer.push("MultiLineTextFile.txt");
|
||||
|
||||
let meta_data_result = path_buffer.metadata();
|
||||
|
||||
match meta_data_result {
|
||||
Ok(meta_data) => {
|
||||
println!("Metadata about the file : {:?}", path_buffer);
|
||||
println!("File creation time {:?}", meta_data.created());
|
||||
println!("File size {}", meta_data.len());
|
||||
assert_eq!(meta_data.len(), 117);
|
||||
println!("File permissions {:?}", meta_data.permissions());
|
||||
assert!(!meta_data.permissions().readonly());
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("Could not get metadata. Error: {:?}", error);
|
||||
}
|
||||
}
|
||||
|
||||
file_cleanup()
|
||||
}
|
||||
|
||||
fn create_required_files() -> Result<(), std::io::Error> {
|
||||
let file_path = PathBuf::from("SampleFilesFolder/MultiLineTextFile.txt");
|
||||
|
||||
let dir_path = match file_path.parent() {
|
||||
Some(parent) => parent,
|
||||
None => return Err(Error::other("Could not get parent path")),
|
||||
};
|
||||
|
||||
if !dir_path.exists() {
|
||||
fs::create_dir(dir_path).inspect_err(|x| {
|
||||
eprintln!("Could not create directory: {:?}", x);
|
||||
})?;
|
||||
}
|
||||
|
||||
if !file_path.exists() {
|
||||
let text = "This is the first line of the text.
|
||||
This is the second line.
|
||||
And this is the third and the last line.";
|
||||
fs::write(&file_path, text).inspect_err(|err| {
|
||||
eprintln!("Couldn't create test file: {:?}", err);
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_cleanup() -> Result<(), std::io::Error> {
|
||||
let mut path_buffer = PathBuf::new();
|
||||
|
||||
path_buffer.push("SampleFilesFolder");
|
||||
path_buffer.push("MultiLineTextFile.txt");
|
||||
|
||||
if path_buffer.exists() {
|
||||
fs::remove_file(&path_buffer).inspect(|_| {
|
||||
println!("Test file removed");
|
||||
})?;
|
||||
}
|
||||
path_buffer.pop();
|
||||
|
||||
if path_buffer.exists() {
|
||||
fs::remove_dir(&path_buffer).inspect(|_| {
|
||||
println!("Test dir removed");
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user