Rust:部分文字列の取得
ググってもクソみたいな解説しか見つからないのでここに書く。
ASCIIのみと分かっている場合は、単純なスライスを作ればよい。(Stringと&strのどちらでも同じ)
code:rs
fn main() {
let text = "ABCDEFG";
let s = &text2..5;
println!("{}", s); // -> CDE
}
String (または &str) の中身は UTF-8 になっていて、バイト単位で格納されている。
インデックス番号はこのバイト位置を示している。
Unicodeのコードポイント(char)として中途半端な場所、範囲を読み出そうとするとエラーになる。
(書記素の分解までは検知しない)
コードポイント単位で分割する場合は chars() で char 単位のイテレータを取り出して、skip(n) で先頭の余計なn文字を読み飛ばし、take(m) で必要なm文字を取り出し、collect() で1つにまとめて String 型にする。
code:rs
fn main() {
let text = "🍣🍺大好き💖💖";
let s = text.chars().skip(2).take(3).collect::<String>();
println!("{}", s); // -> 大好き
}
頻繁にこの処理が行われる場合
code:rust
fn main() {
let text = "🍣🍺大好き💖💖";
let cs = text.chars().collect::<Vec<_>>(); // 先に分解してしまう
let s: String = String::from_iter(&cs2..5);
// let s: String = &cs2..5.iter().collect(); // たぶんこちらは遅い(未測定)
println!("{}", s); // -> 大好き
}
書記素単位で分割する場合
https://crates.io/crates/unicode-segmentation
code:rust
use unicode_segmentation::UnicodeSegmentation;
fn main() {
let text = "กำ🤷🏽‍♀️กำ🤷🏽‍♀️กำ🤷🏽‍♀️กำ";
let s = text.graphemes(true).skip(2).take(3).collect::<Vec<_>>().join(""); // Vec<&str> になってるのをjoinで結合
println!("{}", s); // -> กำ🤷🏽‍♀️กำ
}
code:rs
use unicode_segmentation::UnicodeSegmentation;
fn main() {
let text = "กำ🤷🏽‍♀️กำ🤷🏽‍♀️กำ🤷🏽‍♀️กำ";
let cs = text.graphemes(true).collect::<Vec<_>>();
let s = cs2..5.join("");
println!("{}", s); // -> กำ🤷🏽‍♀️กำ
}
関連
文字列の切り出し
参考
https://stackoverflow.com/questions/58770462/how-to-iterate-over-unicode-grapheme-clusters-in-rust
書記素で分割する方法
Rustで文字列の先頭文字や部分文字列を取得する
https://qiita.com/HelloRusk/items/7fb68395984958987a54
いわゆる素人解説になってて動くには動くが望ましくない
Rustで文字列イテレータを連結するときに便利な itertools::join は結構遅い
https://qiita.com/maguro_tuna/items/003a19f782884a694f8f
StringInfo と TextElementEnumerator は現在 UAX29 に準拠する
https://learn.microsoft.com/ja-jp/dotnet/core/compatibility/globalization/5.0/uax29-compliant-grapheme-enumeration