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();
&str
とString
は、C/C++におけるconst char *
なリテラルと文字列クラスstd::string
みたいなもの
String
と&str
はas_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には含まれないので、ライブラリを使う事。
よく使われる文字列操作からピックアップ
文字数
そのうち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());