This question already has answers here:
How to do simple math with a list of numbers from a file and print out the result in Rust?
(2 answers)
Closed 3 years ago.
I just need to find a way to work with input to do simple maths with it. I've been trying this for a couple of days for the advent of code 2019 day 1
use std::fs::File;
use std::io::{BufRead, BufReader, Error};
fn main() -> Result<(), Error> {
let path = "input.txt";
let input = File::open(path)?;
let buffered = BufReader::new(input);
for line in buffered.lines() {
line.parse::<i32>().unwrap();
line / 2;
println!("{:?}", line);
}
Ok(())
}
If you want your code to compile.
use std::fs::File;
use std::io::{BufRead, BufReader, Error};
fn main() -> Result<(), Error> {
let path = "input.txt";
let input = File::open(path)?;
let buffered = BufReader::new(input);
for line in buffered.lines() {
println!("{:?}", line.unwrap().parse::<i32>().unwrap() / 2);
}
Ok(())
}
Related
I am trying to figure out how to parse a specific line in a file as a u32 but I keep getting method not found in Option<String> when I try to parse a Option<String>.
Is there a way to parse it or am I approaching this wrong?
use std::io::{BufRead, BufReader};
use std::fs::File;
fn main() {
let reader = BufReader::new(File::open("input").expect("Cannot open file"));
let lines = reader.lines();
let number: u32 = lines.nth(5).unwrap().ok().parse::<u32>();
println!("{}", number);
}
You can't parse a number out of an Option<String>, since if it is None then there is nothing to parse. You must unwrap the Option first (or do proper error handling):
use std::io::{BufRead, BufReader};
use std::fs::File;
fn main() {
let reader = BufReader::new(File::open("input").expect("Cannot open file"));
let number: u32 = reader.lines()
.nth(5)
.expect("input is not 5 lines long")
.expect("could not read 5th line")
.parse::<u32>()
.expect("invalid number");
println!("{}", number);
}
This question already has answers here:
How does the Iterator::collect function work?
(2 answers)
Closed 1 year ago.
i want to read a textfile and convert all lines into int values.
I use this code.
But what i really miss here is a "good" way of error handling.
use std::{
fs::File,
io::{prelude::*, BufReader},
path::Path
};
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<i32> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.map(|l:String| l.parse::<i32>().expect("could not parse int"))
.collect()
}
Question: How to do proper error handling ?
Is this above example "good rust code" ?
or should i use something like this :
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<i32> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.map(|l:String| match l.parse::<i32>() {
Ok(num) => num,
Err(e) => -1 //Do something here
}).collect()
}
You can actually collect into a Result<T, E>.
See docs
So you could collect into a Result<Vec<i32>, MyCustomErrorType>.
This works when you transform your iterator in an iterator which returns a Result<i32, MyCustomErrorType>. The iteration stops at the first Err you map.
Here's your working code example.
I used the thiserror crate for error handling
use std::{
fs::File,
io::{prelude::*, BufReader},
num::ParseIntError,
path::Path,
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum LineParseError {
#[error("Failed to read line")]
IoError(#[from] std::io::Error),
#[error("Failed to parse int")]
FailedToParseInt(#[from] ParseIntError),
}
fn lines_from_file(filename: impl AsRef<Path>) -> Result<Vec<i32>, LineParseError> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines().map(|l| Ok(l?.parse()?)).collect()
}
Some small explanation of how the code works by breaking down this line of code:
buf.lines().map(|l| Ok(l?.parse()?)).collect()
Rust infers that we need to collect to a Result<Vec<i32>, LineParseError> because the return type of the function is Result<Vec<i32>, LineParseError>
In the mapping method we write l? this makes the map method return an Err if the l result contains an Err, the #[from] attribute on LineParseError::IoError takes care of the conversion
The .parse()? works the same way: #[from] on LineParseError::FailedToParseInt takes care of the conversion
Last but not least our method must return Ok(...) when the mapping does succeed, this makes the collect into a Result<Vec<i32>, LineParseError> possible.
So basically, I have a text file with the following syntax:
String int
String int
String int
I have an idea how to read the Values if there is only one entry per line, but if there are multiple, I do not know how to do it.
In Java, I would do something simple with while and Scanner but in Rust I have no clue.
I am fairly new to Rust so please help me.
Thanks for your help in advance
Solution
Here is my modified Solution of #netwave 's code:
use std::fs;
use std::io::{BufRead, BufReader, Error};
fn main() -> Result<(), Error> {
let buff_reader = BufReader::new(fs::File::open(file)?);
for line in buff_reader.lines() {
let parsed = sscanf::scanf!(line?, "{} {}", String, i32);
println!("{:?}\n", parsed);
}
Ok(())
}
You can use the BuffRead trait, which has a read_line method. Also you can use lines.
For doing so the easiest option would be to wrap the File instance with a BuffReader:
use std::fs;
use std::io::{BufRead, BufReader};
...
let buff_reader = BufReader::new(fs::File::open(path)?);
loop {
let mut buff = String::new();
buff_reader.read_line(&mut buff)?;
println!("{}", buff);
}
Playground
Once you have each line you can easily use sscanf crate to parse the line to the types you need:
let parsed = sscanf::scanf!(buff, "{} {}", String, i32);
Based on: https://doc.rust-lang.org/rust-by-example/std_misc/file/read_lines.html
For data.txt to contain:
str1 100
str2 200
str3 300
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
fn main() {
// File hosts must exist in current path before this produces output
if let Ok(lines) = read_lines("./data.txt") {
// Consumes the iterator, returns an (Optional) String
for line in lines {
if let Ok(data) = line {
let values: Vec<&str> = data.split(' ').collect();
match values.len() {
2 => {
let strdata = values[0].parse::<String>();
let intdata = values[1].parse::<i32>();
println!("Got: {:?} {:?}", strdata, intdata);
},
_ => panic!("Invalid input line {}", data),
};
}
}
}
}
// The output is wrapped in a Result to allow matching on errors
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
Outputs:
Got: Ok("str1") Ok(100)
Got: Ok("str2") Ok(200)
Got: Ok("str3") Ok(300)
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
new to rust, I'm asking this to hopefully understand how to work with the language better..
Heres me trying to read a file, pass the file handle to a different function and print the lines from there:
use std::fs::File;
use std::io::{self, BufReader};
use std::io::BufRead;
use async_std::task;
async fn scrape<R: BufRead>(reader: &mut R) {
reader.lines().for_each(|line| println!(line));
}
fn main() -> io::Result<()> {
let file = File::open("wlist.txt")?;
let reader = BufReader::new(file);
// print_type_of(&reader); // prints std::io::buffered::BufReader<std::fs::File>
task::block_on(scrape(&mut reader));
Ok(())
}
// helper functions
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
Would love some insights regarding what concepts i'm missing and how this should be done properly !
This version atleast compiles, but there's no async in the playground so I coudn't add async.
You have to catch errors in scrape by using try_for_each function, inside you parse io::Result<String> via and_then. In your main you have to catch all errors explicitly, not allowed to let io::Error slip through ? operator
use std::fs::File;
use std::io::{self, BufReader};
use std::io::BufRead;
//use async_std::task;
fn scrape<R: BufRead>(reader: &mut R) -> io::Result<()> {
reader.lines()
.try_for_each(|lineResult| lineResult.and_then(|line| Ok(println!("{}", line))))?;
Ok(())
}
fn main() {
//let fileResult = File::open("wlist.txt");
//let mut file;
//match fileResult {
// Ok(f) => file = f,
// Err(e) => println!("File open error! {}", e),
//}
let mut file = b"hello\nworld\nhow\nare\nyou\ndoing" as &[u8]; // no file in playground, so just a buffer
let mut reader = BufReader::new(file);
// print_type_of(&reader); // prints std::io::buffered::BufReader<std::fs::File>
match scrape(&mut reader) {
Ok(_) => println!("Scraping is done!"),
Err(e) => println!("File read error! {}", e),
}
}
// helper functions
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::iter::Iterator;
fn main() -> std::io::Result<()> {
let file = File::open("input")?; // file is input
let mut buf_reader = BufReader::new(file);
let mut contents = String::new();
buf_reader.read_to_string(&mut contents)?;
for i in contents.parse::<i32>() {
let i = i / 2;
println!("{}", i);
}
Ok(())
}
list of numbers:
50951
69212
119076
124303
95335
65069
109778
113786
124821
103423
128775
111918
138158
141455
92800
50908
107279
77352
129442
60097
84670
143682
104335
105729
87948
59542
81481
147508
str::parse::<i32> can only parse a single number at a time, so you will need to split the text first and then parse each number one by one. For example if you have one number per line and no extra whitespace, you can use BufRead::lines to process the text line by line:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let file = File::open("input")?; // file is input
let mut buf_reader = BufReader::new(file);
for line in buf_reader.lines() {
let value = line?
.parse::<i32>()
.expect("Not able to parse: Content is malformed !");
println!("{}", value / 2);
}
Ok(())
}
As an extra bonus this avoids reading the whole file into memory, which can be important if the file is big.
For tiny examples like this, I'd read the entire string at once, then split it up on lines.
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let contents = fs::read_to_string("input")?;
for line in contents.trim().lines() {
let i: i32 = line.trim().parse()?;
let i = i / 2;
println!("{}", i);
}
Ok(())
}
See also:
What's the de-facto way of reading and writing files in Rust 1.x?
For tightly-controlled examples like this, I'd ignore errors occurring while parsing:
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let contents = fs::read_to_string("input")?;
for i in contents.trim().lines().flat_map(|l| l.trim().parse::<i32>()) {
let i = i / 2;
println!("{}", i);
}
Ok(())
}
See also:
Why does `Option` support `IntoIterator`?
For fixed-input examples like this, I'd avoid opening the file at runtime at all, pushing the error to compile-time:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let contents = include_str!("../input");
for i in contents.trim().lines().flat_map(|l| l.trim().parse::<i32>()) {
let i = i / 2;
println!("{}", i);
}
Ok(())
}
See also:
Is there a good way to include external resource data into Rust source code?
If I wanted to handle failures to parse but treat the iterator as if errors were impossible, I'd use Itertools::process_results:
use itertools; // 0.8.2
fn main() -> Result<(), Box<dyn std::error::Error>> {
let contents = include_str!("../input");
let numbers = contents.trim().lines().map(|l| l.trim().parse::<i32>());
let sum = itertools::process_results(numbers, |i| i.sum::<i32>());
println!("{:?}", sum);
Ok(())
}
See also:
How do I perform iterator computations over iterators of Results without collecting to a temporary vector?
How do I stop iteration and return an error when Iterator::map returns a Result::Err?