J'ai jeté un œil à Rust docs pour String
mais je ne trouve pas le moyen d'extraire une sous-chaîne.
Existe-t-il une méthode comme substr
de JavaScript dans Rust? Sinon, comment le mettriez-vous en œuvre?
str.substr(start[, length])
Le plus proche est probablement slice_unchecked
mais il utilise des décalages d'octets au lieu d'index de caractères et est marqué unsafe
.
Pour les personnages, vous pouvez utiliser s.chars().skip(pos).take(len)
:
fn main() {
let s = "Hello, world!";
let ss: String = s.chars().skip(7).take(5).collect();
println!("{}", ss);
}
Attention cependant à la définition des caractères Unicode.
Pour les octets, vous pouvez utiliser la syntaxe slice:
fn main() {
let s = "Hello, world!";
let ss = &s[7..12];
println!("{}", ss);
}
Vous pouvez utiliser la méthode as_str
sur l'itérateur Chars
pour récupérer une tranche &str
après l'avoir remplacé. Donc, pour sauter les premiers caractères start
, vous pouvez appeler
let s = "Some text to slice into";
let mut iter = s.chars();
iter.by_ref().nth(start); // eat up start values
let slice = iter.as_str(); // get back a slice of the rest of the iterator
Maintenant, si vous voulez aussi limiter la longueur, vous devez d'abord déterminer la position d'octet du caractère length
:
let end_pos = slice.char_indices().nth(length).map(|(n, _)| n).unwrap_or(0);
let substr = &slice[..end_pos];
Cela peut sembler un peu détourné, mais Rust ne vous cache rien qui puisse prendre des cycles de processeur. Cela dit, je me demande pourquoi il n’ya pas encore de caisse qui offre une méthode substr
.
Pour la syntaxe de type my_string.substring(start, len)
-, vous pouvez écrire un trait personnalisé:
trait StringUtils {
fn substring(&self, start: usize, len: usize) -> Self;
}
impl StringUtils for String {
fn substring(&self, start: usize, len: usize) -> Self {
self.chars().skip(start).take(len).collect()
}
}
// Usage:
fn main() {
let phrase: String = "this is a string".to_string();
println!("{}", phrase.substring(5, 8)); // prints "is a str"
}
Ce code effectue à la fois une sous-chaîne et une division en chaîne, sans paniquer ni allouer:
use std::ops::{Bound, RangeBounds};
trait StringUtils {
fn substring(&self, start: usize, len: usize) -> &str;
fn slice(&self, range: impl RangeBounds<usize>) -> &str;
}
impl StringUtils for str {
fn substring(&self, start: usize, len: usize) -> &str {
let mut char_pos = 0;
let mut byte_start = 0;
let mut it = self.chars();
loop {
if char_pos == start { break; }
if let Some(c) = it.next() {
char_pos += 1;
byte_start += c.len_utf8();
}
else { break; }
}
char_pos = 0;
let mut byte_end = byte_start;
loop {
if char_pos == len { break; }
if let Some(c) = it.next() {
char_pos += 1;
byte_end += c.len_utf8();
}
else { break; }
}
&self[byte_start..byte_end]
}
fn slice(&self, range: impl RangeBounds<usize>) -> &str {
let start = match range.start_bound() {
Bound::Included(bound) | Bound::Excluded(bound) => *bound,
Bound::Unbounded => 0,
};
let len = match range.end_bound() {
Bound::Included(bound) => *bound + 1,
Bound::Excluded(bound) => *bound,
Bound::Unbounded => self.len(),
} - start;
self.substring(start, len)
}
}
fn main() {
let s = "abcdèfghij";
// All three statements should print:
// "abcdè, abcdèfghij, dèfgh, dèfghij."
println!("{}, {}, {}, {}.",
s.substring(0, 5),
s.substring(0, 50),
s.substring(3, 5),
s.substring(3, 50));
println!("{}, {}, {}, {}.",
s.slice(..5),
s.slice(..50),
s.slice(3..8),
s.slice(3..));
println!("{}, {}, {}, {}.",
s.slice(..=4),
s.slice(..=49),
s.slice(3..=7),
s.slice(3..));
}