Rustの文字列操作

  • 40
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Rustにおいて、文字列関係の型は3種類ある。

文字を表すchar

charはユニコードのコードポイントと一対一に対応する

let ch: char = 'a';
assert_eq!(ch as u32, 0x61);  // 32bit整数にキャスト出来る

文字列リテラルは&str

文字列版のスライス
なので、変更可能ではない

let s: &str = "文字列";

通常の変更可能な文字列クラスはString

let string: String = String::new();

&strStringは、C/C++におけるconst char *なリテラルと文字列クラスstd::stringみたいなもの

String&stras_slice()to_string()で交互に変換できる。
ただし、to_string()は新しくStringを作り、文字列をコピーする。as_slice()は参照するだけなのでコストは小さい

"abc".to_string().as_slice();

Rustの文字列はutf-8の文字列でなければならない
もし変なバイトが含まれているとエラーになる

String::from_utf8(b"\xbb".to_vec()).unwrap();  // Error!

文字列操作で気をつける点

2つの&strを簡単にくっつける方法は無い

リテラルならconcat!マクロを使うとコンパイル時にまとめてくれる

println!("{}", concat!("abc", "def", 0, 1.2));

リテラル以外の場合はformat!()が簡単でおすすめ

println!("{}", format!("{}{}", "abc", "def"));

左側がStringで、かつ過去を振り返らないなら"+"演算子が使える

let (a, b) = ("abc".to_string(), "def");
println!("{}", a + b);  // 今後aを参照するとコンパイルエラー

結論としては、concat!format!を使うのが良い。+演算子は使える条件が厳しいので使わないほうが良い。
例外として、mutableなStringに文字列を追加する時に使ったり出来るが、同じ意味のpush_str()を使うほうがいいと思う

let mut c = "a".to_string();
c = c + "b";
c.push_str("c");  // push()は一文字足す、push_str(&str)は文字列を足す

文字列のスライス操作はなるべく避け、使うときは特徴を把握する

文字列はUTF-8で格納されているので、slice_chars(i, j)O(j)のコストがかかる。

assert_eq!("い", "あいう".slice_chars(1, 2));

s[i]のように気軽にインデックスアクセス出来ない。先頭と末尾は次のようにすると楽。

assert_eq!('あ', "あいう".char_at(0));
assert_eq!('う', "あいう".char_at_reverse("あいう".len()));

もし頻繁にインデックスアクセスを行いたい場合には、chars()で文字配列を作れば良い

"あいう".chars().collect::<Vec<char>>();

中のバイト列が欲しい時にはbytes()やas_bytes()を使う

println!("s[0](byte)={}", s.as_bytes()[0]);

文字列のフォーマットはformat!()マクロで。println!()と同じsyntaxで使える

assert_eq!("0.123", format!("{:.3}", 0.123456));

正規表現は組み込みじゃない

stdには含まれないので、ライブラリを使う事。

https://github.com/rust-lang/regex

よく使われる文字列操作からピックアップ

文字数

そのうちchar_len()が使えるようになるかも

assert_eq!(3, "あいう".chars().count());

文字列の結合(join)

assert_eq!("Hello World", ["Hello", "World"].connect(" "));

こちらは単に結合するだけ。一般のVecに対するflatternメソッドとして使える

let d: String = ["Hello", "World"].concat();
assert_eq!("HelloWorld", d);

文字列の分割

"a.b.c".split('.');  // => iterator of ["a", "b", "c"]
"a..b..c".split_str(".");  // => iterator of ["a", "b", "c"]

置換

assert_eq!("adeade", "abcabc".replace("bc", "de"));  // replaceはin-place演算ではない。新しい文字列を作って返す

部分文字列が含まれているか調べる

assert!("abc".starts_with("ab"));
assert!(!"abc".starts_with("cb"));
assert!("abcd".contains("bc"));
assert_eq!(Some(2), "abcde".find_str("cd"));

部分文字列を得る

assert_eq!("b", "abc".slice_chars(1, 2));

部分文字列の出現回数をカウントする

対応するメソッドがないのでmatch_indicesを介する

assert_eq!(2, "xabxxabxxbc".match_indices("ab").count());

trim/strip

assert_eq!("abc1def", "11abc1def11".trim_matches('1'));
assert_eq!("a", "  a".trim_left());