Help us understand the problem. What is going on with this article?

Rustの文字列操作

More than 5 years have passed since last update.

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());
aflc
仕事で自然言語処理やってます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away