简体   繁体   English

如何组合std :: str :: lines和std :: io :: lines?

[英]How to combine std::str::lines and std::io::lines?

I want to write a function to parse text, but the text may come from external file or an internal &str . 我想编写一个函数来解析文本,但文本可能来自外部文件或内部&str The parse function may go like this: parse函数可能如下所示:

fn parse(lines: GenericLinesGenerator) {
   for line in lines {
       // parse content
   }
}

... and it can be invoked like this: ......它可以像这样调用:

use std::io::BufReader;
use std::fs::File;
let fin = BufReader::new(File::open("info.txt").expect("not found"));
parse(TransformFromIO(fin.lines()))

or 要么

let content: &'static str = "some\nlong\ntext";
parse(TransformFromStr(content.lines()))

Is it possible to implement such a parse function? 是否可以实现这样的parse功能?

The two iterators don't produce the same values: 这两个迭代器不会产生相同的值:

impl<B: BufRead> Iterator for io::Lines<B> {
    type Item = Result<String>;
}
impl<'a> Iterator for str::Lines<'a> {
    type Item = &'a str;
}

You have to handle that difference somehow. 你必须以某种方式处理这种差异。 The most important difference is that io::Lines can fail . 最重要的区别是io::Lines可能会失败 Your program has to decide how to deal with that; 你的程序必须决定如何处理; I've chosen to just abort the program. 我选择了中止该计划。

The next thing you need to do is to accept any type that can be converted into an iterator, and the value yielded from the iterator has to be converted to a type that you can deal with. 接下来你需要做的是接受任何可以转换为迭代器的类型,并且必须将迭代器产生的值转换为你可以处理的类型。 It appears that &str is the common denominator. 似乎&str是共同点。

This is solved by using IntoIterator and Borrow : 这可以通过使用IntoIteratorBorrow来解决:

use std::borrow::Borrow;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

fn parse<I>(lines: I)
where
    I: IntoIterator,
    I::Item: Borrow<str>,
{
    for line in lines {
        println!("line: {}", line.borrow());
    }
}

fn main() {
    parse("alpha\nbeta\ngamma".lines());

    println!("----");

    let f = File::open("/etc/hosts").expect("Couldn't open");
    let b = BufReader::new(f);
    parse(b.lines().map(|l| l.expect("Bad line!")));
}

Check The Rust Programming Language section on where clauses for more information about trait bounds . 有关特征边界的更多信息,请参阅where子句 的Rust编程语言部分。

Using the Borrow bound in the parse function will allow you to borrow &str , but if you need String values, a better approach is to use Cow . 使用parse函数中的Borrow bound将允许你借用&str ,但是如果你需要String值,更好的方法是使用Cow

Using line.borrow().to_string() to obtain a String value will always allocate, even when parse is called with lines from file (in this case, lines.map produces String ). 使用line.borrow().to_string()获取String值将始终分配,即使用文件中的行调用parse (在这种情况下, lines.map生成String )。

Using line.into_owned() will allocate when called with lines from an &str , but will not allocate when called with lines from file (will just unwrap the String value passed to Cow::Owned ). 当使用来自&str行调用时,将使用line.into_owned()进行分配,但是当使用来自文件的行调用时将不会分配(将仅解包传递给Cow::OwnedString值)。

use std::borrow::Cow;
use std::io::{BufReader, BufRead};
use std::iter::IntoIterator;
use std::fs::File;

fn parse<'a, I>(lines: I)
    where I: IntoIterator,
          I::Item: Into<Cow<'a, str>>
{
    for line in lines {
        let line: Cow<'a, str> = line.into();
        let line: String = line.into_owned();
        // or
        let line = line.into().into_owned()
        println!("{}", line);
    }
}

fn main() {
    let fin = BufReader::new(File::open("/etc/hosts").expect("cannot open file"));
    parse(fin.lines().map(|r| r.expect("file read failed")));

    let content: &'static str = "some\nlong\ntext";
    parse(content.lines());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM