Chars and Substrings

Буквы (char)

Перевод букв в числа методом to_digit(RADIX) (RADIX=10 в десятичной системе) и их сумма:

fn main() {
    const RADIX: u32 = 10;
    let x = "134";
    println!("{}", x.chars().map(|c| c.to_digit(RADIX).unwrap()).sum::<u32>()); }

Перевод букв в коды ASCII и обратно:

println!("{}", 'a' as u8); // перевод символа в код ASCII
println!("{}", 97 as char); // число как символ

// перевод кода UTF-8 в символ (не все символы можно перевести):
println!("{:?}", std::char::from_u32(98).unwrap()); 

Первая и последняя буква в строке

Чтобы проверить или изменить 1-ую букву в строке (в том числе иероглиф или иной вариант алфавита), нужно строку переделать в вектор из букв:

    let char_vec: Vec<char> = text.chars().collect();
    if char_vec[0].is_lowercase() { .. }

Для последней буквы есть метод last(), вешается на итератор chars(), возвращает Option<char>, так как последней буквы может и не быть:

println!("{}", "foobar".chars().last().unwrap()); // 'r'

Гласные / согласные буквы

Проверку нужно написать в виде функции:

fn is_vowel(c: char) -> bool {  
    c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ||  
    c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' }

let text = String::from("Aria");

Сортировка букв в словах

Есть функции sort() и sort_unstable() (работает быстрее, но равные элементы может перемешивать) для вектора символов Vec<char>. Обе функции делают сортировку на месте, возвращают ().

let input = "zxyabc"; // сортировка Unicode тоже работает
let mut char2vec_iter = input.chars()
    .collect::<Vec<char>>();
char2vec_iter.sort_unstable(); // сортировка на месте
// char2vec_iter.sort_by(|a,b| b.cmp(a)); // реверс-сортировка

// собираем назад String из Vec<char>
let sorted_string: String = char2vec_iter.into_iter().collect();
println!("{}", sorted_string); // "abcxyz"

chunks() и chunks_exact() разбиение строк на части

Аналогично массивам, строки можно разбить на части:

let text = "ПриветМир";
let chars = text.chars().collect::<Vec<char>>();

println!("Разбиваем строку на части по 3 символа:");
for chunk in chars.chunks(3) {
    let s: String = chunk.iter().collect();
    println!("{}", s); 
// При
// вет
// Мир

Строковые срезы

Срезы позволяют взять часть строки, указав диапазон байтовых индексов.

  • Синтаксис&string[start..end] (где start — начало, end — конец, не включительно).
  • Особенность: нужно вручную следить за границами байтов, иначе будет паника при попытке разрезать строку посреди многобайтового символа.
let text = "Hello, world!"; // тип &str
let text = String::from("Hello, world!"); // String
// Если у вас String, а не &str, нужно взять срез с помощью &:
let first_three = &text[0..3];        // первые 3 символа
let last_five = &text[text.len()-5..]; // последние 5 символов
println!("{}", first_three); // "Hel"
println!("{}", last_five);   // "orld!"

Отрицательные индексы в Rust не поддерживаются, поэтому нужно вычислять вручную. Если указать только начало ([start..]), берётся всё до конца:

let world_and_more = &text[7..];
println!("{}", world_and_more); // "world!"

Если строка содержит многобайтовые символы (например, кириллицу или эмодзи), простые байтовые срезы могут вызвать панику. Для работы с символами (Unicode scalar values) используйте метод .chars():

let text = "Привет, мир!";
let chars: Vec = text.chars().collect(); // преобразуем в вектор символов
let privet: String = chars[0..6].iter().collect(); // собираем первые 6 символов
println!("{}", privet); // "Привет"

Метод .chars() возвращает итератор по символам, а .collect() собирает их в нужный тип (например, String).

.get(start..end) для безопасного извлечения

Если нет уверенности в границах, и надо избежать паники, подойдёт метод .get() вместо прямого среза. Он возвращает Option<&str>:

let text = "Hello, world!";
if let Some(substr) = text.get(0..5) {
    println!("{}", substr); // "Hello"
} else {
    println!("Ошибка: неверные границы"); }

Библиотека substring 

Для более прямого аналога substr можно использовать стороннюю библиотеку substring из crates.io. Добавьте в Cargo.toml:

[dependencies]
substring = "1.4.5"

Пример использования:

use substring::Substring;

let text = "Hello, world!";
let hello = text.substring(0, 5);
println!("{}", hello); // "Hello"
let world = text.substring(7, 12);
println!("{}", world); // "world"
  • Преимущество: проще для новичков, не нужно беспокоиться о байтовых границах.
  • Недостаток: добавляет внешнюю зависимость.

Примеры

Разворот букв в словах

Дана строка с пробелами между словами. Необходимо развернуть слова в строке наоборот, при этом сохранить пробелы.

fn reverse_words_split(str: &str) -> String {  
    str.to_string()
    .split(" ") // при разделении split() множественные пробелы сохраняются
    .map(|x| x.chars().rev().collect::<String>()) // разворот слов+сбор в строку
    .collect::<Vec<String>>().                    // сбор всего в вектор
    .join(" ")                                    // превращение вектора в строку
}

fn main() {  
    let word: &str = "The   quick brown fox jumps over the lazy dog.";  
    println!("{}",reverse_words_split(&word));  
}

// ehT   kciuq nworb xof spmuj revo eht yzal .god