2015-08-27 9 views
9

Gibt es eine einfache Möglichkeit, eine Zeichenkette unter Beibehaltung der Trennzeichen zu teilen? Statt dessen:Eine Zeichenkette aufteilen, wobei die Trennzeichen beibehalten werden

let texte = "Ten. Million. Questions. Let's celebrate all we've done together."; 
let v: Vec<&str> = texte.split(|c: char| !(c.is_alphanumeric() || c == '\'')).filter(|s| !s.is_empty()).collect(); 

, die mit ["Ten", "Million", "Questions", "Let's", "celebrate", "all", "we've", "done", "together"] führt.

würde ich etwas, das mir gibt:

["Ten", ".", " ", "Million", ".", " ", "Questions", ".", " ", "Let's", " ", "celebrate", " ", "all", " ", "we've", " ", "done", " ", "together", "."].

ich diese Art von Code bin versucht (die Zeichenfolge annimmt, beginnt mit einem Buchstaben und endet mit einem ‚non'-Brief):

let texte = "Ten. Million. Questions. Let's celebrate all we've done together. "; 
let v1: Vec<&str> = texte.split(|c: char| !(c.is_alphanumeric() || c == '\'')).filter(|s| !s.is_empty()).collect(); 
let v2: Vec<&str> = texte.split(|c: char| c.is_alphanumeric() || c == '\'').filter(|s| !s.is_empty()).collect(); 
let mut w: Vec<&str> = Vec::new(); 

let mut j = 0; 
for i in v2 { 
    w.push(v1[j]); 
    w.push(i); 
    j = j+1; 
} 

Es mir, dass ich früher schrieb fast das Ergebnis gibt, aber es ist gut :

["Ten", ". ", "Million", ". ", "Questions", ". ", "Let's", " ", "celebrate", " ", "all", " ", "we've", " ", "done", " ", "together", "."] 

Gibt es jedoch eine bessere Möglichkeit, das zu programmieren? Weil ich versucht habe, auf v2 aufzuzählen, aber es hat nicht funktioniert, und es sieht grob aus, j in der for-Schleife zu verwenden.

+1

Regex ist, was Sie brauchen. – Onilol

Antwort

3

war ich nicht in der Lage etwas in der Standard-Bibliothek zu finden, so I wrote my own:

Diese Version verwendet die instabilen Muster API wie es flexibler ist, aber der Link hat oben einen Rückfall, den ich für meine spezifischen fest einprogrammiert habe stabiler Anwendungsfall

#![feature(pattern)] 

use std::str::pattern::{Pattern,Searcher}; 

#[derive(Copy,Clone,Debug,PartialEq)] 
pub enum SplitType<'a> { 
    Match(&'a str), 
    Delimiter(&'a str), 
} 

pub struct SplitKeepingDelimiter<'p, P> 
    where P: Pattern<'p> 
{ 
    searcher: P::Searcher, 
    start: usize, 
    saved: Option<usize>, 
} 

impl<'p, P> Iterator for SplitKeepingDelimiter<'p, P> 
    where P: Pattern<'p>, 
{ 
    type Item = SplitType<'p>; 

    fn next(&mut self) -> Option<SplitType<'p>> { 
     if self.start == self.searcher.haystack().len() { 
      return None; 
     } 

     if let Some(end_of_match) = self.saved.take() { 
      let s = &self.searcher.haystack()[self.start..end_of_match]; 
      self.start = end_of_match; 
      return Some(SplitType::Delimiter(s)); 
     } 

     match self.searcher.next_match() { 
      Some((start, end)) => { 
       if self.start == start { 
        let s = &self.searcher.haystack()[start..end]; 
        self.start = end; 
        Some(SplitType::Delimiter(s)) 
       } else { 
        let s = &self.searcher.haystack()[self.start..start]; 
        self.start = start; 
        self.saved = Some(end); 
        Some(SplitType::Match(s)) 
       } 
      }, 
      None => { 
       let s = &self.searcher.haystack()[self.start..]; 
       self.start = self.searcher.haystack().len(); 
       Some(SplitType::Match(s)) 
      }, 
     } 
    } 
} 

pub trait SplitKeepingDelimiterExt: ::std::ops::Index<::std::ops::RangeFull, Output = str> { 
    fn split_keeping_delimiter<P>(&self, pattern: P) -> SplitKeepingDelimiter<P> 
     where P: for <'a> Pattern<'a> 
    { 
     SplitKeepingDelimiter { searcher: pattern.into_searcher(&self[..]), start: 0, saved: None } 
    } 
} 

impl SplitKeepingDelimiterExt for str {} 

#[cfg(test)] 
mod test { 
    use super::{SplitKeepingDelimiterExt}; 

    #[test] 
    fn split_with_delimiter() { 
     use super::SplitType::*; 
     let delims = &[',', ';'][..]; 
     let items: Vec<_> = "alpha,beta;gamma".split_keeping_delimiter(delims).collect(); 
     assert_eq!(&items, &[Match("alpha"), Delimiter(","), Match("beta"), Delimiter(";"), Match("gamma")]); 
    } 

    #[test] 
    fn split_with_delimiter_allows_consecutive_delimiters() { 
     use super::SplitType::*; 
     let delims = &[',', ';'][..]; 
     let items: Vec<_> = ",;".split_keeping_delimiter(delims).collect(); 
     assert_eq!(&items, &[Delimiter(","), Delimiter(";")]); 
    } 
} 

Sie werden bemerken, dass ich verfolgen benötigt, wenn etwas eine der Begrenzungszeichen war oder nicht, aber das sollte, wenn Sie es nicht brauchen, anzupassen einfach sein.

+0

Wow, ich muss mehr über Rust erfahren, um diesen Code zu verstehen. Allerdings dachte ich nach dem Teilen der Saite zweimal mit dem Muster, um Wörter und dann das entgegengesetzte Muster zu bekommen. Was denkst du über meinen neuen Code? – Keho

+1

Dies wird einfacher, wenn ['str :: match_indices'] (http://doc.rust-lang.org/nightly/std/primitive.str.html#method.match_indices) stabil wird. – bluss

3

Mit str::match_indices:

let text = "Ten. Million. Questions. Let's celebrate all we've done together."; 

let mut result = Vec::new(); 
let mut last = 0; 
for (index, matched) in text.match_indices(|c: char| !(c.is_alphanumeric() || c == '\'')) { 
    if last != index { 
     result.push(&text[last..index]); 
    } 
    result.push(matched); 
    last = index + matched.len(); 
} 
if last < text.len() { 
    result.push(&text[last..]); 
} 

println!("{:?}", result); 

Drucke:

["Ten", ".", " ", "Million", ".", " ", "Questions", ".", " ", "Let\'s", " ", "celebrate", " ", "all", " ", "we\'ve", " ", "done", " ", "together", "."]

Verwandte Themen