Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read multiple lines from stdin until EOF?

Tags:

c++

rust

How can I read multiple lines from stdin and advance until EOF in Rust? I'm trying to translate this C++ code to Rust but I'm failing.

#include <iostream>

int main()
{    
    try {
        std::string line;
        
        while (std::getline(std::cin, line)) {
            int length = std::stoi(line);
            
            for (int i = 0; i < length; i++) {
                std::string input;
                std::getline(std::cin, input);
                
                std::cout << input << std::endl;
            }
        }
    } catch (std::invalid_argument const&) {
        std::cout << "invalid input" << std::endl;
    }
    return 0;
}

The program is fed with this input. It starts reading the first line which is a number indicating the total number of lines following, then tries reading all these lines and advancing to the next number until it terminates (or finds an EOF?).

4
a
bb
ccc
dddd
5
eeeee
dddd
ccc
bb
a

This is my Rust code. It compiles but it seems like it gets stuck in an infinite loop. I'm running it with program < lines.txt in terminal. What I'm doing wrong?

use std::io::{self, BufRead};

fn main() -> io::Result<()> { 
    let stdin = io::stdin();
    
    for line in stdin.lock().lines() {
        let length: i32 = line.unwrap().trim().parse().unwrap();
        
        for _ in 0..length {
            let line = stdin.lock()
                .lines()
                .next()
                .expect("there was no next line")
                .expect("the line could not be read");
            
            println!("{}", line);
        }
    }
    
    Ok(())
}

1 Answers

The problem is you're calling stdin.lock() twice which immediately deadlocks. That and you'll need to use only a single .lines() call because its designed to consume the entire input. Fortunately, that just means we have to refactor the outer for loop into a while:

use std::io::{self, BufRead};

fn main() -> io::Result<()> {
    let stdin = io::stdin();
    let mut lines = stdin.lock().lines();

    while let Some(line) = lines.next() {
        let length: i32 = line.unwrap().trim().parse().unwrap();

        for _ in 0..length {
            let line = lines
                .next()
                .expect("there was no next line")
                .expect("the line could not be read");

            println!("{}", line);
        }
    }

    Ok(())
}
like image 69
kmdreko Avatar answered Sep 02 '25 16:09

kmdreko