Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read, filter and modify lines from a file

Tags:

io

filter

rust

How to do something similar to this D and Java code in Rust?

Java:

import java.nio.file.*;
import java.io.*;

public class Main {
    public static void main( String[] args ) throws IOException
    {
        Files.lines(Paths.get("/home/kozak/test.txt"))
            .filter(s -> s.endsWith("/bin/bash"))
            .map(s -> s.split(":", 2)[0])
            .forEach(System.out::println);
    }
}

D language:

import std.algorithm;
import std.stdio;

void main() {
    File("/home/kozak/test.txt")
        .byLine
        .filter!((s)=>s.endsWith("/bin/bash"))
        .map!((s)=>s.splitter(":").front)
        .each!writeln;
}

I try it, but I am lost with all this ownership stuff

my rust code:

use std::io::BufReader;
use std::fs::File;
use std::io::BufRead;
use std::io::Lines;

fn main() {
    let file = match File::open("/etc/passwd") {
        Ok(file) => file,
        Err(..)  => panic!("room"),
    };

    let mut reader = BufReader::new(&file);
    for line in reader.lines().filter_map(
        |x| if match x { Ok(v) => v.rmatches("/bin/bash").count() > 0, Err(e) => false}
        { match x { Ok(v2) => Some(v2.split(":").next()), Err(e2) => None }} else
        { None })
    { 
            print!("{}", line.unwrap() );

    }
}
like image 993
Kozzi11 Avatar asked May 19 '15 14:05

Kozzi11


1 Answers

Here you go:

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = BufReader::new(File::open("/etc/passwd").unwrap());
    let it = f.lines()
        .map(|line| line.unwrap())
        .filter(|line| line.ends_with("/bin/bash"))
        .map(|line| line.split(":").next().unwrap().to_owned());
    for p in it {
        println!("{}", p);
    }
}

This code allocates a separate string for each first splitted part though, but I don't think it is possible to avoid it without streaming iterators. And, of course, error handling here is really lax.

I guess an imperative approach would be more idiomatic, especially in regard to error handling:

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = BufReader::new(File::open("/etc/passwd").unwrap());
    for line in f.lines() {
        match line {
            Ok(line) => if line.ends_with("/bin/bash") {
                if let Some(name) = line.split(":").next() {
                    println!("{}", name);
                } else {
                    println!("Line does not contain ':'");
                }
            },
            Err(e) => panic!("Error reading file: {}", e)
        }
    }
}
like image 191
Vladimir Matveev Avatar answered Nov 18 '22 01:11

Vladimir Matveev