Добрый день.
Нахожусь в поиске библиотеки для работы с регулярками. Гугл так или иначе приводит к regex. Её метод find_at выдаёт начало найденого текста относительно начала строки в байтах, а мне надо в символах. Например есть строка привет мне надо что бы find выдавал начало подстроки ивет со 2-го символа а не с 4-го байта. Как мне подойти к решению задачи?
Обычные строки, str и String, в rust кодируются в utf-8. Символ utf-8 (можно грубо считать что буква), имеет длину от 1 до 4 байт, в зависимости от количества единиц в начале первого байта:
1 байт - 0xxxxxxx
2 байта - 110xxxxx
3 байта - 1110xxxx
4 байта - 11110xxx
Т.е. чтобы получить подстроку можно отсечь n-ное количество байт, высчитав их количество по первому байту символа. Возможно уже существует какой-то небольшой крейт для этого, но можно и самому написать функцию.
Тут еще вопрос - что считать символом. Хз можно ли это сделать как-то красивее или эффективнее, но я бы для начала попробовал итерироваться по отступам графемных кластеров при помощи grapheme_indices
и параллельно считать их.
Ясно, спасибо за ответы. Думал может есть путь попроще чем погружаться в тонкости unicode и кодировок. У меня задача учебная, попробую обойтись без регулярок и запрограммировать конечный автомат руками, перегнав строку в Vec<char>
.
Да тут не так что бы особо нюансы, вроде. Я вот что-то такое имел в виду как стартовую точку:
use regex::Regex;
use unicode_segmentation::UnicodeSegmentation as US;
fn index_byte_to_grapheme(s: &str, index: usize) -> Option<usize> {
for (grapheme_i, (byte_i, _grapheme)) in US::grapheme_indices(s, true).enumerate() {
// dbg!((grapheme_i, byte_i, _grapheme));
if byte_i == index {
return Some(grapheme_i);
}
}
None
}
fn main() {
let s = "абвгд";
let re = Regex::new("вг").unwrap();
let mat = re.find_at(s, 0).unwrap();
dbg!(mat);
let start_grapheme_i = index_byte_to_grapheme(s, mat.start());
let end_grapheme_i = index_byte_to_grapheme(s, mat.end());
dbg!(start_grapheme_i);
dbg!(end_grapheme_i);
}
Если будете использовать только ASCII, то ASCII является подмножеством utf-8, и поэтому с ним смело можно работать как с однобайтовым char массивом. Совсем просто будет.
Если только с ascii то понятно, мне было интересно как быть когда надо хотя бы с кирилицей работать. Сталкиваюсь с rust впервые, в других языках, таких как python или Go обычно в стандартной библиотеке есть такие вещи.
Так и Rust есть, непонятно зачем unicode_segmentation
,
можно вызвать метод char_indices
у &str
и понять какой char соответствует найденному смещению байтах.
Если только с кириллицей надо работать и всё, то графемные кластеры тебе не актуальны и хватит работы с кодпоинтами. Как @Dushistov пишет - с этим справятся и встроенные char’ы языка.
непонятно зачем
unicode_segmentation
Для нормальной работы с юникодом (см ссылку на статью выше)? Например, что бы эмоджи из нескольких кодпоинтов не взрывали код - изначально же в вопросе не было обозначено, что нужна работа только с кириллицей.