I want to read a file and get back a vector of Strings. The following function works, but is there a more concise or idiomatic way?
use std::fs::File; use std::io::Read; fn lines_from_file(filename: &str) -> Vec<String> { let mut file = match File::open(filename) { Ok(file) => file, Err(_) => panic!("no such file"), }; let mut file_contents = String::new(); file.read_to_string(&mut file_contents) .ok() .expect("failed to read!"); let lines: Vec<String> = file_contents.split("\n") .map(|s: &str| s.to_string()) .collect(); lines } Some things that seem suboptimal to me:
String, which will be thrown away. This would be particularly wasteful if I only wanted the first N lines.&str per line, which will be thrown away, instead of somehow going straight from the file to a String per line.How can this be improved?
In Java, we can store the content of the file into an array either by reading the file using a scanner or bufferedReader or FileReader or by using readAllLines method.
Check fptr as return value of open() if it's NULL decide what to do. Remove unnecessary tot variable and use another index j in last for loop. Use strncpy() as a better version of strcpy() Correct way of print arr, printf("%s\n", arr[j]);
DK.'s answer is quite right and has great explanation. However, you stated:
Read a file and get an array of strings
Rust arrays have a fixed length, known at compile time, so I assume you really mean "vector". I would write it like this:
use std::{ fs::File, io::{prelude::*, BufReader}, path::Path, }; fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> { let file = File::open(filename).expect("no such file"); let buf = BufReader::new(file); buf.lines() .map(|l| l.expect("Could not parse line")) .collect() } // --- fn main() { let lines = lines_from_file("/etc/hosts"); for line in lines { println!("{:?}", line); } } AsRef for the filename.Result::expect shortens the panic on Err.BufRead::lines handles multiple types of newlines, not just "\n".BufRead::lines also gives you separately allocated Strings, instead of one big glob.Vec<String>).If you wanted to return a Result on failure, you can squash the implementation down to one line if you want:
use std::{ fs::File, io::{self, BufRead, BufReader}, path::Path, }; fn lines_from_file(filename: impl AsRef<Path>) -> io::Result<Vec<String>> { BufReader::new(File::open(filename)?).lines().collect() } // --- fn main() { let lines = lines_from_file("/etc/hosts").expect("Could not load lines"); for line in lines { println!("{:?}", line); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With