【问题标题】:Read lines from file, iterate over each line and each character in that line从文件中读取行,遍历每一行和该行中的每个字符
【发布时间】:2015-08-27 17:32:03
【问题描述】:

我需要读取一个文件,获取每一行,遍历每一行并检查该行是否包含“aeiuo”中的任何字符以及是否包含至少 2 个字符“äüö”。

这段代码是惯用的 Rust 吗?如何检查 String 中的多个字符?

到目前为止,我在一些 Google 和代码窃取方面的尝试:

use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;

fn main() {
    // Create a path to the desired file
    let path = Path::new("foo.txt");
    let display = path.display();

    // Open the path in read-only mode, returns `io::Result<File>`
    let file = match File::open(&path) {
        // The `description` method of `io::Error` returns a string that describes the error
        Err(why) => panic!("couldn't open {}: {}", display, Error::to_string(&why)),
        Ok(file) => file,
    };

    // Collect all lines into a vector
    let reader = BufReader::new(file);
    let lines: Vec<_> = reader.lines().collect();

    for l in lines {
        if (l.unwrap().contains("a")) {
            println!("here is a");
        }
    }
}

(Playground link)

【问题讨论】:

  • 这听起来像是两个问题:“我的代码是否好/惯用”和“如何在字符串中查找多个字符”。这些应该分开;后者对于 Stack Overflow 来说是个好问题,但第一个问题应该提交给 Code Review - 确保遵守他们的提交规则!那里需要工作代码。
  • 我同意@Shepmaster 但我想回答这个问题:pastebin.com/6uSBrFzQ
  • 谢谢,我会用这个改进我的代码:)。

标签: loops character line rust contains


【解决方案1】:

这行得通:

use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;

fn is_vowel(x: &char) -> bool {
    "aAeEiIoOuU".chars().any(|y| y == *x)
}

fn is_umlaut(x: &char) -> bool {
    "äÄüÜöÖ".chars().any(|y| y == *x)
}

fn valid(line: &str) -> bool {
    line.chars().all(|c| !is_vowel(&c)) && line.chars().filter(is_umlaut).fuse().nth(1).is_some()
}

fn main() {
    // Create a path to the desired file
    let path = Path::new("c.txt");
    let display = path.display();
    // Open the path in read-only mode, returns `io::Result<File>`
    let file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };
    let reader = BufReader::new(file);
    for line in reader.lines() {
        match line {
            Ok(line) => {
                if valid(&line) {
                    println!("{}", line)
                }
            }
            Err(e) => println!("ERROR: {}", e),
        }
    }
}

【讨论】:

    【解决方案2】:

    1) “这段代码是惯用的 Rust 吗?”

    总体不错,看起来不错。您可能需要改进一点:您不需要将线条收集到一个向量中来对其进行迭代。这是不需要的,因为它会触发不需要的内存分配。只需直接阅读 lines() 迭代器即可。 (如果你来自 C++,你可以忘记将东西收集到中间向量中:想想函数,想想迭代器!)

    let reader = BufReader::new(file);
    let lines: Vec<_> = reader.lines().collect();
    
    for l in lines {
        ...
    }
    

    变成

    let reader = BufReader::new(file);
    let lines = reader.lines(); 
    // lines is a instance of some type which implements Iterator<Item=&str>
    
    for l in lines {
        ...
    }
    

    2) “如何检查字符串中的多个字符?”

    我建议一个基于.any()的简单方法:

    fn is_aeiou(x: &char) -> bool {
        "aeiou".chars().any(|y| y == *x)
    }
    
    fn is_weird_auo(x: &char) -> bool {
        "äüö".chars().any(|y| y == *x)
    }
    
    fn valid(line: &str) -> bool {
        line.chars().any(|c| is_aeiou(&c)) &&
        line.chars().filter(is_weird_auo).fuse().nth(1).is_some()
    }
    

    然后你可以一路迭代,编写你的主要测试如下:

    let reader = BufReader::new(file);
    let lines = reader.lines();
    
    let bad_line = lines.map(|l| l.unwrap()).filter(|line| !valid(line)).next();
    match bad_line {
        Some(line_n) => println!("Line {} doesn't pass the test", line_n),
        None => println!("All lines are good!"),
    }
    
    // Alternate way if you don't need the line number. More readable
    //let all_good = lines.map(|l| l.unwrap()).all(valid);
    

    (完整代码在playground。)

    【讨论】:

    • 谢谢,我是一个爱好 java 程序员,整个 .iter().filter().map() 东西对我来说都是新的,还没有任何函数式编程。如果没有 IDE 中的自动完成功能,所有不同的选项都很难记住。
    • 在这种情况下,请随意阅读和重新阅读Iterator docs,这很棒!
    • 我遇到了一个奇怪的错误 - 我把你的代码编译了,到目前为止没有问题,但是我的测试文件“foo.txt”包含“üü”、“ää”、“aöö”我在控制台中得到的前 3 行:“没有通过测试”。带有“Line {}”的部分被切断。将行更改为“未通过测试 {}”给我“未通过测试?üü”,?激怒了我,“ää”没有出现?
    • 此解决方案不正确:它无法处理“äüö”:这些是使用多个代码点的 分解 形式。您应该在进行比较时对输入进行规范化,并且您应该使用 graphemes 迭代器 not chars 迭代器(为此,您需要 crates.io 的 unicode-segmentation 包,除非你想使用夜间和不稳定的功能)。
    • @Vega:不幸的是,这不是一件容易的事,因为 Unicode 很复杂。我建议阅读Wikipedia 上的代码点字形组合字符序列,你会明白你的String 可能是复杂的。至于显示字符串的问题,您确定您的终端正确配置为显示 UTF-8 吗?
    猜你喜欢
    • 2020-02-02
    • 2010-11-30
    • 1970-01-01
    • 2018-08-24
    • 2014-06-29
    • 1970-01-01
    • 2021-02-19
    • 1970-01-01
    相关资源
    最近更新 更多