diff --git a/exercises/24_file_io/README.md b/exercises/24_file_io/README.md new file mode 100644 index 00000000..a6ef28a9 --- /dev/null +++ b/exercises/24_file_io/README.md @@ -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) diff --git a/exercises/24_file_io/file_io1.rs b/exercises/24_file_io/file_io1.rs new file mode 100644 index 00000000..54647dc4 --- /dev/null +++ b/exercises/24_file_io/file_io1.rs @@ -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(()) +} diff --git a/exercises/24_file_io/file_io2.rs b/exercises/24_file_io/file_io2.rs new file mode 100644 index 00000000..f19d2a47 --- /dev/null +++ b/exercises/24_file_io/file_io2.rs @@ -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(()) +} diff --git a/exercises/24_file_io/file_io3.rs b/exercises/24_file_io/file_io3.rs new file mode 100644 index 00000000..7966bfcc --- /dev/null +++ b/exercises/24_file_io/file_io3.rs @@ -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(()) +} diff --git a/solutions/24_file_io/file_io1.rs b/solutions/24_file_io/file_io1.rs new file mode 100644 index 00000000..9331eeea --- /dev/null +++ b/solutions/24_file_io/file_io1.rs @@ -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(()) +} diff --git a/solutions/24_file_io/file_io2.rs b/solutions/24_file_io/file_io2.rs new file mode 100644 index 00000000..bfcaff1e --- /dev/null +++ b/solutions/24_file_io/file_io2.rs @@ -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(()) +} diff --git a/solutions/24_file_io/file_io3.rs b/solutions/24_file_io/file_io3.rs new file mode 100644 index 00000000..a68dad6e --- /dev/null +++ b/solutions/24_file_io/file_io3.rs @@ -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(()) +}