contest_algorithms/
scanner.rs

1//! Generic utility for reading data from standard input, based on [voxl's
2//! stdin wrapper](http://codeforces.com/contest/702/submission/19589375).
3use std::io;
4use std::str;
5
6/// Reads white-space separated tokens one at a time.
7pub struct Scanner<R> {
8    reader: R,
9    buffer: Vec<String>,
10}
11
12impl<R: io::BufRead> Scanner<R> {
13    pub fn new(reader: R) -> Self {
14        Self {
15            reader,
16            buffer: vec![],
17        }
18    }
19
20    /// Use "turbofish" syntax token::<T>() to select data type of next token.
21    ///
22    /// # Panics
23    ///
24    /// Panics if there's an I/O error or if the token cannot be parsed as T.
25    pub fn token<T: str::FromStr>(&mut self) -> T {
26        loop {
27            if let Some(token) = self.buffer.pop() {
28                return token.parse().ok().expect("Failed parse");
29            }
30            let mut input = String::new();
31            self.reader.read_line(&mut input).expect("Failed read");
32            self.buffer = input.split_whitespace().rev().map(String::from).collect();
33        }
34    }
35}
36
37/// Same API as Scanner but nearly twice as fast, using horribly unsafe dark arts
38/// **REQUIRES** Rust 1.34 or higher
39pub struct UnsafeScanner<R> {
40    reader: R,
41    buf_str: Vec<u8>,
42    buf_iter: str::SplitAsciiWhitespace<'static>,
43}
44
45impl<R: io::BufRead> UnsafeScanner<R> {
46    pub fn new(reader: R) -> Self {
47        Self {
48            reader,
49            buf_str: vec![],
50            buf_iter: "".split_ascii_whitespace(),
51        }
52    }
53
54    /// This function should be marked unsafe, but noone has time for that in a
55    /// programming contest. Use at your own risk!
56    pub fn token<T: str::FromStr>(&mut self) -> T {
57        loop {
58            if let Some(token) = self.buf_iter.next() {
59                return token.parse().ok().expect("Failed parse");
60            }
61            self.buf_str.clear();
62            self.reader
63                .read_until(b'\n', &mut self.buf_str)
64                .expect("Failed read");
65            self.buf_iter = unsafe {
66                let slice = str::from_utf8_unchecked(&self.buf_str);
67                std::mem::transmute(slice.split_ascii_whitespace())
68            }
69        }
70    }
71}
72
73pub fn scanner_from_file(filename: &str) -> Scanner<io::BufReader<std::fs::File>> {
74    let file = std::fs::File::open(filename).expect("Input file not found");
75    Scanner::new(io::BufReader::new(file))
76}
77
78pub fn writer_to_file(filename: &str) -> io::BufWriter<std::fs::File> {
79    let file = std::fs::File::create(filename).expect("Output file not found");
80    io::BufWriter::new(file)
81}
82
83#[cfg(test)]
84mod test {
85    use super::*;
86
87    fn solve<R: io::BufRead, W: io::Write>(scan: &mut Scanner<R>, out: &mut W) {
88        let x = scan.token::<i32>();
89        let y = scan.token::<i32>();
90        writeln!(out, "{} - {} = {}", x, y, x - y).ok();
91    }
92
93    fn unsafe_solve<R: io::BufRead, W: io::Write>(scan: &mut UnsafeScanner<R>, out: &mut W) {
94        let x = scan.token::<i32>();
95        let y = scan.token::<i32>();
96        writeln!(out, "{} - {} = {}", x, y, x - y).ok();
97    }
98
99    #[test]
100    fn test_in_memory_io() {
101        let input: &[u8] = b"50 8";
102        let mut scan = Scanner::new(input);
103        let mut out = vec![];
104
105        solve(&mut scan, &mut out);
106        assert_eq!(out, b"50 - 8 = 42\n");
107    }
108
109    #[test]
110    fn test_in_memory_unsafe() {
111        let input: &[u8] = b"50 8";
112        let mut scan = UnsafeScanner::new(input);
113        let mut out = vec![];
114
115        unsafe_solve(&mut scan, &mut out);
116        assert_eq!(out, b"50 - 8 = 42\n");
117    }
118
119    #[test]
120    fn test_compile_stdio() {
121        let (stdin, stdout) = (io::stdin(), io::stdout());
122        let mut scan = Scanner::new(stdin.lock());
123        let mut out = io::BufWriter::new(stdout.lock());
124
125        if false {
126            solve(&mut scan, &mut out);
127        }
128    }
129
130    #[test]
131    #[should_panic(expected = "Input file not found")]
132    fn test_panic_file() {
133        let mut scan = scanner_from_file("input_file.txt");
134        let mut out = writer_to_file("output_file.txt");
135
136        solve(&mut scan, &mut out);
137    }
138}