2015-08-27 2 views
9

Есть ли тривиальный способ разделить строку, удерживающую разделители? Вместо этого:Разделите строку, удерживающую разделители

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(); 

что приводит с ["Ten", "Million", "Questions", "Let's", "celebrate", "all", "we've", "done", "together"].

Я хотел бы что-то, что дает мне:

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

Я пытаюсь что-то код (он принимает строку начинается с буквы и заканчивается «non'-буквы):

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; 
} 

Это дает мне почти результат я писал раньше, но это хорошо :

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

Однако есть ли лучший способ кодировать это? Поскольку я пытался перечислить v2, но это не сработало, и выглядит грубо использовать j в цикле for.

+1

Regex является то, что вам нужно. – Onilol

ответ

3

Я не смог найти что-либо в стандартной библиотеке, так I wrote my own:

Эта версия использует нестабильный шаблон API, как это более гибкая, но ссылка выше, имеет запасной вариант, который я жёстко для моего специфического стабильная usecase.

#![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(";")]); 
    } 
} 

Вы заметите, что мне нужно, чтобы отслеживать, если что-то один из разделителей или нет, но это должно быть легко адаптироваться, если она не нужна.

+0

Wow, мне нужно узнать больше о Rust, чтобы понять этот код. Однако я подумал после того, как дважды разделил строку с шаблоном, чтобы получить слова, а затем противоположный шаблон. Что вы думаете о моем новом коде? – Keho

+1

Это будет проще, если ['str :: match_indices'] (http://doc.rust-lang.org/nightly/std/primitive.str.html#method.match_indices) станет стабильным. – bluss

3

Использование 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); 

Печать:

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

Смежные вопросы