Как в Rust получить количество символов в строке, а не байтов?
Проблема: байты vs символы в Rust
В Rust строки (String и &str) хранятся в кодировке UTF-8. Метод .len() возвращает количество байтов, а не символов. Это сделано для производительности — байтовый счёт работает за O(1). Но для подсчёта символов (Unicode scalar values) нужно использовать другой подход.
Решение: метод .chars().count()
Самый простой и правильный способ — использовать итератор .chars(), который перебирает символы (char) строки, а затем вызвать .count():
fn main() {
let text = String::from("Привет, мир! 😊");
// Подсчёт символов
let char_count = text.chars().count();
println!("Символов: {}", char_count); // 14 (включая эмодзи)
// Для сравнения — байтовая длина
println!("Байтов: {}", text.len()); // 22
}
Важные детали
- Сложность:
.chars().count()работает за O(n), где n — количество байт. Это линейный проход по строке. Для коротких строк это незаметно, но для очень длинных (миллионы символов) стоит учитывать. - Unicode и графемы:
.chars()считает именно Unicode scalar values (char). Но некоторые "символы" (например, эмодзи 🇷🇺 или буквы с диакритическими знаками, как é) могут состоять из нескольких char. Для подсчёта "видимых" символов (графемных кластеров) используйте крейтunicode-segmentation. - Пустая строка:
"".chars().count()вернёт 0, как и ожидается.
Пример с графемными кластерами
Если вам нужно считать именно то, что видит пользователь (например, флаг страны — один символ с точки зрения пользователя, но два char в UTF-8), добавьте в Cargo.toml:
[dependencies]
unicode-segmentation = "1.10"
И используйте:
use unicode_segmentation::UnicodeSegmentation;
fn main() {
let text = "🇷🇺 Привет!";
// Количество char (Unicode scalar values)
println!("Chars: {}", text.chars().count()); // 4 (флаг занимает 2 char + пробел + 6 букв)
// Количество графемных кластеров (то, что видит пользователь)
let graphemes = text.graphemes(true).count();
println!("Graphemes: {}", graphemes); // 9 (флаг как 1 + пробел + 6 букв + !)
}
Советы для начинающих
- Не пытайтесь использовать
.len()для подсчёта символов — это частая ошибка. - Если вы пишете библиотеку, документируйте, что метод считает именно chars, а не байты.
- Для проверки длины ввода пользователя (например, ограничение в 140 символов) используйте
.chars().count()— это корректно обработает эмодзи и иероглифы.
Итог: text.chars().count() — стандартный способ получить количество символов в Rust. Для большинства задач этого достаточно. Если нужно работать с графемами — подключайте unicode-segmentation.
Альтернативный подход: использование метода char_indices и ручной подсчёт
Если вы по каким-то причинам не хотите использовать итератор .chars() (например, в очень старом коде или при необходимости одновременно получать индексы), можно сделать подсчёт вручную через char_indices():
fn count_chars(s: &str) -> usize {
s.char_indices().count()
}
fn main() {
let text = "Здравствуй, мир!";
println!("{}", count_chars(text)); // 16
}
Метод char_indices() возвращает итератор кортежей (usize, char), где первый элемент — байтовый индекс начала символа, второй — сам символ. Просто вызывая .count(), мы получаем количество символов.
Когда это может быть полезно?
- Если вам нужны не только символы, но и их позиции в байтах (например, для обрезки строки по символам).
- В учебных целях — чтобы лучше понять, как устроены строки в Rust.
Однако для простого подсчёта символов .chars().count() короче и читаемее. Используйте char_indices() только если вам действительно нужны индексы.
Важно: Оба метода работают с Unicode scalar values, а не с графемами. Для подсчёта "видимых" символов (как в первом ответе) используйте крейт unicode-segmentation.
Ответов пока нет.
Войдите чтобы оставить ответ