Reading stdin line by line selectively

I'm attempting to use rust to tackle so Competitive Programming problems, but I'm having issues parsing input from stdin such as this:

2

abx
asdf
asdfq
we

adx
fsdaf
ffsd

Where the first line indicates the number of test cases and each test case is separated by a blank line.

My initial approach is to read all of stdin into a Std::io::BufReader like:
let buffer = BufReader::new(io::stdin())

I understand how to I can loop through each line of the buffer using something like:
let buffer = BufReader::new(reader);
for line in buffer.lines() {
println!("{}", line?)
}

However, like in C++, I'm looking for some way where I can manually advance the buffer.lines() iterator, so that when I being my for loop, I'll be starting at the first word input, "abx". I anticipated that this would've given me the desired behavior:

let buffer = BufReader::new(io::stdin());
let mut input_iter = buffer.lines();
input_iter.next();
input_iter.next();

for line in input_iter {
    println!("{:?}", line);
}

But the for loop in this case prints every line. I'm also curious if there are better means to ultimately achieve my goal as well. Thank you!

Whenever you use a for loop like that, it's going to read every remaining line. Since you can only use the standard library during competitive programming, I like to set up something like this:

use std::io::{stdin, BufReader, BufRead};
use std::error::Error;

struct Input<B> {
    inner: B,
    buffer: String,
}
impl<B: BufRead> Input<B> {
    pub fn new(inner: B) -> Self {
        Self {
            inner,
            buffer: String::new(),
        }
    }
    pub fn line(&mut self) -> Line {
        self.buffer.clear();
        self.inner.read_line(&mut self.buffer).unwrap();
        Line {
            split: self.buffer.split_whitespace()
        }
    }
}
struct Line<'a> {
    split: std::str::SplitWhitespace<'a>
}
impl<'a> Line<'a> {
    fn next(&mut self) -> u32 {
        self.split.next().unwrap().parse().unwrap()
    }
    fn pair(&mut self) -> (u32, u32) {
        let a = self.next();
        let b = self.next();
        (a, b)
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let input = stdin();
    let mut input = Input::new(BufReader::new(input.lock()));

    let test_cases = input.line().next();

    for _ in 0..test_cases {
        let (n, k) = input.line().pair();

        let mut best = 0;
        for _ in 0..n {
            let mut line = input.line();
            let sum = (0..k).map(|_| line.next()).sum();
            if sum > best {
                best = sum;
            }
        }
        println!("{}", best);
    }
    Ok(())
}

I find that this makes it easier to use. The example above goes through a number of test cases and for each test case it sums each line and prints the largest sum.

1 Like

If the input is not super large, you can combine read_to_string and split_ascii_whitespace, which is also pretty easy to use.

1 Like

Thanks for your advice! I'm still reading through the rust book so a few things look unfamiliar to me:

  1. In inmp<B: BufRead> Input, how come there is a nested Self {} block?
  2. Is the "tick a" lifetime specifier necessary in this case? Or just being explicit?
1 Like

The Self {} block could also be written Input {}. I'm simply creating an instance of the type inside the new method.

The lifetime is necessary, because the SplitWhitespace type contains a reference to the string in Input.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.