2016-05-04 5 views
3

Ich möchte eine Funktion zum Parsen von Text schreiben, aber der Text kann von einer externen Datei oder einem internen &str stammen. Die parse Funktion kann so gehen:Wie std :: str :: lines und std :: io :: lines zu kombinieren?

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

... und es kann wie folgt aufgerufen werden:

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

oder

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

Ist es möglich, zu implementieren, eine solche Funktion parse ?

Antwort

4

Die beiden Iteratoren produzieren nicht die gleichen Werte:

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

Sie haben irgendwie diesen Unterschied zu behandeln. Der wichtigste Unterschied ist, dass io::Lines fehlschlagen kann. Ihr Programm muss entscheiden, wie Sie damit umgehen sollen. Ich habe mich entschieden, das Programm einfach abzubrechen.

Als nächstes müssen Sie einen beliebigen Typ akzeptieren, der in einen Iterator konvertiert werden kann, und der vom Iterator erhaltene Wert muss in einen Typ konvertiert werden, mit dem Sie umgehen können. Es scheint, dass &str der gemeinsame Nenner ist.

Dies wird durch IntoIterator und Borrow unter Verwendung gelöst:

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!"))); 
} 

prüfen The Rust Programming Language Abschnitt where clauses für weitere Informationen über Charakterzug begrenze.

+0

Super! Vielen Dank! – knight42

+0

Es ist überraschend, dass Sie kein neues Merkmal für ein solches Problem implementieren müssen. – WiSaGaN

2

die Borrow in der parse Funktion gebunden verwenden können Sie &str leihen, aber wenn Sie String Werte benötigen, ein besserer Ansatz ist Cow zu verwenden.

line.borrow().to_string() Mit einem String Wert erhalten wird immer vergeben, auch wenn parse mit Zeilen aus der Datei aufgerufen wird (in diesem Fall lines.map produziert String).

Mit line.into_owned() wird vergeben, wenn sie mit Zeilen aus einem &str genannt, wird aber nicht zuteilen, wenn sie mit Zeilen aus Datei mit dem Namen (wird nur das zu Cow::Owned geben String Wert auspacken).

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()); 
} 
Verwandte Themen