Jak podzielić ciąg w Rust?

143

Z dokumentacji nie wynika jasno. W Javie możesz użyć takiej splitmetody:

"some string 123 ffd".split("123");
ア レ ッ ク ス
źródło
@bow Czy istnieje sposób, aby uczynić z niej tablicę String zamiast wektora?
Greg
Nie znam żadnego sposobu, żeby to zrobić, przynajmniej bezpośrednio. Prawdopodobnie musiałbyś ręcznie wykonać iterację Spliti ustawić go w tablicy. Oczywiście oznacza to, że liczba elementów w każdym podziale musi być taka sama, ponieważ tablice mają stały rozmiar i musisz mieć wcześniej zdefiniowaną tablicę. Wyobrażam sobie, że może to być większy problem niż zwykłe utworzenie pliku Vec.
ukłon

Odpowiedzi:

158

Posługiwać się split()

let mut split = "some string 123 ffd".split("123");

Daje to iterator, który można zapętlić lub collect()do wektora.

for s in split {
    println!("{}", s)
}
let vec = split.collect::<Vec<&str>>();
// OR
let vec: Vec<&str> = split.collect();
Manishearth
źródło
15
Możesz też to napisać .collect::<Vec<_>>().
Chris Morgan,
jak uzyskać długość wyniku - let split? split.len()nie istnieje.
ア レ ッ ク ス
5
@AlexanderSupertramp Use .count(). len()jest tylko dla iteratorów, które znają swój dokładny rozmiar bez konieczności używania, count()zużywa iterator.
Manishearth
error: cannot borrow immutable local variable split` as mutable`
ア レ ッ ク ス
@AlexanderSupertramp let mut split, przepraszam.
Manishearth
53

Istnieją trzy proste sposoby:

  1. Przez separator :

    s.split("separator")  |  s.split('/')  |  s.split(char::is_numeric)
  2. Według białych znaków :

    s.split_whitespace()
  3. Przez nowych linii :

    s.lines()

Wynikiem każdego rodzaju jest iterator:

let text = "foo\r\nbar\n\nbaz\n";
let mut lines = text.lines();

assert_eq!(Some("foo"), lines.next());
assert_eq!(Some("bar"), lines.next());
assert_eq!(Some(""), lines.next());
assert_eq!(Some("baz"), lines.next());

assert_eq!(None, lines.next());
DenisKolodin
źródło
29

Istnieje specjalna metoda splitdla structString :

fn split<'a, P>(&'a self, pat: P) -> Split<'a, P> where P: Pattern<'a>

Podziel według znaku:

let v: Vec<&str> = "Mary had a little lamb".split(' ').collect();
assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]);

Podziel według łańcucha:

let v: Vec<&str> = "lion::tiger::leopard".split("::").collect();
assert_eq!(v, ["lion", "tiger", "leopard"]);

Dzielone przez zamknięcie:

let v: Vec<&str> = "abc1def2ghi".split(|c: char| c.is_numeric()).collect();
assert_eq!(v, ["abc", "def", "ghi"]);
Denis Kreshikhin
źródło
14

splitZwraca Iterator, które można przekonwertować na Vecużyciu collect: split_line.collect::<Vec<_>>(). Przechodzenie przez iterator zamiast zwracania Vecbezpośrednio ma kilka zalet:

  • splitjest leniwy. Oznacza to, że tak naprawdę nie podzieli linii, dopóki jej nie potrzebujesz. W ten sposób nie będzie tracić czasu podział cały ciąg jeśli potrzebujesz tylko kilka pierwszych wartości: split_line.take(2).collect::<Vec<_>>()albo nawet, jeśli potrzebujesz tylko pierwszą wartość, która może być zamienione na liczbę całkowitą: split_line.filter_map(|x| x.parse::<i32>().ok()).next(). Ten ostatni przykład nie marnuje czasu na próby przetworzenia „23.0”, ale przerwie przetwarzanie natychmiast po znalezieniu „1”.
  • splitnie przyjmuje żadnych założeń co do sposobu, w jaki chcesz zapisać wynik. Możesz użyć a Vec, ale możesz także użyć wszystkiego, co implementuje FromIterator<&str>, na przykład a LinkedListlub a VecDeque, lub dowolnego typu niestandardowego, który implementuje FromIterator<&str>.
Jmb
źródło
1
Dziękuję za szczegółową odpowiedź, wszelkie pomysły dlaczego let x = line.unwrap().split(",").collect::<Vec<_>>();nie działają, chyba że są podzielone na dwie osobne linie: let x = line.unwrap();i let x = x.split(",").collect::<Vec<_>>();? Komunikat o błędzie brzmi:temporary value created here ^ temporary value dropped here while still borrowed
Greg
Jednak działa zgodnie z oczekiwaniami, jeśli używam let x = line.as_ref().unwrap().split(",").collect::<Vec<_>>();
Greg
6

Jest także split_whitespace()

fn main() {
    let words: Vec<&str> = "   foo   bar\t\nbaz   ".split_whitespace().collect();
    println!("{:?}", words);
    // ["foo", "bar", "baz"] 
}
jayelm
źródło