Rustでインデックスを指定して文字列を分割しようとして引っかかりました。
ドキュメントを読んで解決したのですが、いちいち検索して英語のドキュメントを読み直すのもあれですので、調べた事などを忘備録代わりに書いておきます。
広告
目次
インデックス指定上の注意点
インデックスは英数字を扱う点では特に問題はないのですが、日本語を使う場合はバイトサイズも頭に入れて使用する必要があります。
たとえば一文字分割したいだけでも、
let mut orig = String::from("あああ");
let a = orig.split_off(3);
println!("{:?}, {:?}", orig,a);
}
のようにする必要があります(日本語は一文字3バイトを使用するため、一文字で配列のインデックスを3つ消費するような感じになる)。
ちなみにこのようなインデックス指定における注意を払っていない場合、panicが巻き起こります。
対策としては
- charで分割・イテレータを取得し、この記事で記すような関数とはまた別の方法で分割処理を実装する
- 切り出したい文字列のバイトサイズをあらかじめ計算して指定する
- 何かの文字列で分割したい場合はその文字列までのインデックスをあらかじめfindで取得しておく
といった感じになると思います。
私の場合、findで見つけてそこで分割、というところだったので、この記事にメモした関数でどうにかなりました。
Stringから分割
split_offを使う(元の文字列が削られる)
Stringに定義されているsplit_off関数を使用すると、そのStringの中身が2分割され、後半部分を格納したStringが返されますので、これにより文字列を分割することができます。
定義をかみ砕くと、
let 分割後半 = String型の変数.split_off(usize型のインデックス)
以下みたいな感じで、引数として与えられたインデックスまでの文字列になるよう元の変数に格納された文字列を削り、削った部分をString型で返します。
let mut orig = String::from(“hogefoo”);
let foo = orig.split_off(4);
println!(“orig:{:?}, foo:{:?}”, orig, foo); //出力: orig:”hoge”, foo:”foo”
注意事項として、分割後も上の例でいうorigのcapacity(ヒープ上に確保されるバッファサイズ)は変わりません。capacityを分割後の文字列長と同じにしたい場合は、shrink_to_fit等を用いて調節を行う必要があります。
split_atを使う(分割結果として&strを2つ取得)
Stringに定義されているsplit_at関数を用いると、元のStringは変更せず、分割結果として&strを2つ得ることができます。
以下みたいな感じで、引数として与えられたインデックスで文字列を分割します。なお元の文字列を保持していた変数の中身は変化しません。
let orig = String::from(“hogefoo”);
let (f, s) = orig.split_at(4);
println!(“{}, {}, {:?}”, f, s, orig); //出力: hoge, foo, “hogefoo”
分割したい場合はこれが大分楽ではありますが、分割後はstrになりますので、文字列結合を行いたい場合など、結局Stringを新しく生成する必要がある場合などは、Stringの柔軟性が欲しい場合はsplit_offでしょうか。検索処理ぐらいならstrで十分だと思います。
popを使う(末尾を一文字ずつ取得する形での分割)
Stringで、単に末尾一文字だけほしいという場合はpopが適当かと思います。popではOption<char>型が返ってきて、もし文字列が空の場合は(末尾から取り出すことすらできませんので)Noneになります。
使い方は以下のような感じ。
let mut orig = String::from(“hogefoo”);
let p = orig.pop();
println!(“{:?}, {:?}”, orig, p); //出力: “hogefo”, Some(‘o’)
let mut a = String::default();
let q = a.pop();
println!(“{:?},{:?}”, a, q); //出力: “”,None
remove(一文字を切り出し)
末尾ではなく、任意のインデックスの一文字を切り出す場合はremove関数を使用して、引数にそのインデックスを与えて分割します。
ただ、(Stringの中身がVec<u8>であることを考えると)たぶんこの処理インデックスの数字が小さくなるほど、文字列が長くなるほどpopよりコストが高くなるかと思われます。
また、文字列長以上のインデックスを指定するとpanicになります。
let mut orig = String::from(“hogefoo”);
let p = orig.remove(4);
println!(“{:?}, {:?}”, orig, p); //出力: “hogeoo”, ‘f’
strもしくは&strを分割
split_atを使う
Stringで使用した関数と同名のsplit_at関数を使うと、Stringと同じように2つの&strを得ることができます。
以下のコードのように、Stringとだいたい同じ感じでstrを分割します。
let orig = “hogefoo”;
let (f,s) = orig.split_at(4);
println!(“{}, {}, {:?}”, f,s,orig); //出力: hoge, foo, hogefoo
単に文字列末尾を削りたいとき(String向け)
truncateを用いる
文字列末尾を削り、任意の長さに文字列を変えたい場合は、truncateを使います。一文字だけ削りたい時は上述したpop関数を用いるとよいでしょう。
let mut orig = String::from(“hogefoo”);
orig.truncate(4);
println!(“{:?}”, orig); //出力: “hoge”