【Rust】文字列の基本と便利なメソッドの使い方

Rustでは文字列を扱う型が2種類あります。

ざっくりと「可変な文字列」と「不変な文字列」の2つになります。

今回はそんなRustにおける文字列について見ていきたいと思います。

今までと同様、自身の勉強のアウトプット・整理と同時に、同じようにRustを勉強している方や「公式ドキュメントはちょっと、、」というような方が気楽に見れるようにサンプルコードをメインに簡単にまとめてみました。

この記事でできること
  • String, &strの基本
  • String, &strの便利なメソッドとその使い方
目次

基本

Rustにおける文字列は以下の2種類があります。

  • String
  • &str

使い方は以下のようになります。

fn sample() {
    let string_from = String::from("string_from");
    let string_to = "string_to".to_string();
    println!("{}", string_from); // -> string_from
    println!("{}", string_to);   // -> string_to

    let str = "str";
    println!("{}", str);         // -> str
}

2つの文字列型の違い

2つの文字列の違いは大きく「可変」か「不変」かというところです。
もう少し詳しく見てみると以下のような違いがあります。

&str
  • 文字列への参照(基本的には不変)
  • 文字列スライスとも呼ばれる
  • プリミティブ型
  • 文字列リテラルは静的な値となる

サンプルコード

fn sample() {
    let string: String = String::from("string");

    // 上記Stringのスライス(参照)
    let str: &str = &string;

    // 文字列リテラルは静的なライフタイム=静的領域に配置された文字列のスライス
    let static_str: &'static str = "static";
}
String
  • 標準ライブラリで定義されている型
  • 他言語における文字列クラスのようなもの
  • mutをつけることでミュータブル(変更可能)な文字列となる

サンプルコード

fn sample() {
    let string: String = String::from("string");
    // string.push('!'); // mutableではないので変数自身を変更する操作はできない

    let mut mut_string: String = String::from("mutable string");
    mut_string.push('!'); // mutableの場合は変数自身を変更する操作も可能
}
参考リンク

四則演算

文字列においては多言語と同様に加算による連結が可能。

fn sample() {
    let string1: String = String::from("string1");
    let string2: String = String::from("string2");

    let str1: &str = "str1";
    let str2: &str = "str2";

    println!("{}", string1 + str2);
    println!("{}", string1);           // 加算を行った変数は所有権を失うのでエラーとなる

    println!("{}", string1 + string2); // 右辺は&strでないとエラー
    println!("{}", str1 + str2);       // &strに対しては加算できない
    println!("{}", str1 + string2)     // &strに対しては加算できない
}

Stringに対しては加算できるが&strに対しては加算できないことに注意。

便利なメソッド(共通)

&str,Stringのいずれにも使用できるメソッドをいくつかピックアップします。

len

文字列のバイト数を取得する。

fn sample() {
    let string: String = String::from("テスト");
    println!("{}", string.len()); // -> 9

    let str: &str = "テスト2";
    println!("{}", str.len()); // -> 10
}

文字数を取得したい場合は下記のようにcharsを使用する。

fn sample() {
    let string: String = String::from("テスト");
    println!("{}", string.chars().count()); // -> 3

    let str: &str = "テスト2";
    println!("{}", str.chars().count()); // -> 4
}

is_empty

文字列が空白かどうかを判定する。

fn sample() {
    let string1: String = String::from("テスト");
    println!("{}", string1.is_empty()); // -> false
    let string2: String = String::from("");
    println!("{}", string2.is_empty()); // -> true

    let str1: &str = "テスト2";
    println!("{}", str1.is_empty()); // -> false
    let str2: &str = "";
    println!("{}", str2.is_empty()); // -> true
}

lines

文字列を改行区切りのiteratorとして取得する。

対応している文字コードは\n,\r\nの2種類。\rには対応していない。

fn sample() {
    let string1: String = String::from("タイトル\n内容\n内容2");
    for line in string1.lines() {
        println!("{}", line);
        // タイトル
        // 内容
        // 内容2
    }

    let str1: &str = "line1\nline2\r\nline3\rline4";
    for line in str1.lines() {
        println!("{}", line);
        // line1
        // line2
        // line4
    }
}

contains

対象の文字列に引数として指定した文字列が存在するか判定する。

fn sample() {
    let string: String = String::from("start dummy end");
    println!("{}", string.contains("dummy")); // -> true
    println!("{}", string.contains("rust"));  // -> false

    let str: &str = "start dummy end";
    println!("{}", str.contains("dummy")); // -> true
    println!("{}", str.contains("rust"));  // -> false
}

start_with

対象の文字列が引数として指定した文字列で始まるかを判定する。

fn sample() {
    let string: String = String::from("start dummy end");
    println!("{}", string.starts_with("start")); // -> true
    println!("{}", string.starts_with("rust"));  // -> false

    let str: &str = "start dummy end";
    println!("{}", str.starts_with("start")); // -> true
    println!("{}", str.starts_with("rust"));  // -> false
}

end_with

対象の文字列が引数として指定した文字列で始まるか/終わるかを判定する。

fn sample() {
    let string: String = String::from("start dummy end");
    println!("{}", string.ends_with("end"));  // -> true
    println!("{}", string.ends_with("rust")); // -> false

    let str: &str = "start dummy end";
    println!("{}", str.ends_with("end"));     // -> true
    println!("{}", str.ends_with("rust"));    // -> false
}

split系

split

指定した文字で文字列を分割する。

fn sample() {
    let string: String = String::from("a,b,c,d,e");
    println!("{:?}", string.split(',').collect::<Vec<&str>>());
    // -> ["a", "b", "c", "d", "e"]

    let str: &str = "a,b,c,d,e";
    println!("{:?}", str.split(',').collect::<Vec<&str>>());
    // -> ["a", "b", "c", "d", "e"]
}
split_whitespace

文字列を空白区切りで分割する。

全角スペースも区切り対象となる。

fn sample() {
    let string: String = String::from("a b\tc\r\nd e");
    println!("{:?}", string.split_whitespace().collect::<Vec<&str>>());
    // -> ["a", "b", "c", "d", "e"]

    let str: &str = "a b\tc\r\nd e";
    println!("{:?}", str.split_whitespace().collect::<Vec<&str>>());
    // -> ["a", "b", "c", "d", "e"]
}

空白として区別されるものはさまざまですが、おもに改行・スペース・タブが空白として認識されます。

split_ascii_whitespace

文字列を空白区切りで分割する。

こちらは全角スペースは区切り対象とならないことに注意。

fn sample() {
    let string: String = String::from("a b\tc\r\nd e");
    println!(
        "{:?}",
        string.split_ascii_whitespace().collect::<Vec<&str>>()
    );
    // -> ["a", "b", "c", "d\u{3000}e"]

    let str: &str = "a b\tc\r\nd e";
    println!("{:?}", str.split_ascii_whitespace().collect::<Vec<&str>>());
    // -> ["a", "b", "c", "d\u{3000}e"]
}
split_terminator

指定した文字で文字列を分割する。

splitとの違いは分割後の末尾が空の場合は無視されること。

fn sample() {
    let string: String = String::from("a,b,c,");
    println!("{:?}", string.split(',').collect::<Vec<&str>>());
    // -> ["a", "b", "c", ""]
    println!("{:?}", string.split_terminator(',').collect::<Vec<&str>>());
    // -> ["a", "b", "c"]

    let str: &str = "a,b,c,";
    println!("{:?}", str.split(',').collect::<Vec<&str>>());
    // -> ["a", "b", "c", ""]
    println!("{:?}", str.split_terminator(',').collect::<Vec<&str>>());
    // -> ["a", "b", "c"]
}
splitn

指定した文字で文字列を分割する。

splitとの違いは、最大n個に分割すること。

fn sample() {
    let string: String = String::from("a,b,c,d,e");
    println!("{:?}", string.splitn(3, ',').collect::<Vec<&str>>());
    // -> ["a", "b", "c,d,e"]

    let str: &str = "a,b,c,d,e";
    println!("{:?}", str.splitn(3, ',').collect::<Vec<&str>>());
    // -> ["a", "b", "c,d,e"]
}

mathces

指定した文字にマッチした文字列を抽出する。

char::is***を利用すると簡単にパターンマッチできるので便利。

fn sample() {
    let string: String = String::from("a,b,c,1,2d,e");
    println!("{:?}", string.matches("2d").collect::<Vec<&str>>());
    // -> ["2d"]
    println!("{:?}", string.matches(char::is_alphabetic).collect::<Vec<&str>>());
    // -> ["a", "b", "c", "d", "e"]

    let str: &str = "a,b,c,1,2d,e";
    println!("{:?}", str.matches("a").collect::<Vec<&str>>());
    // -> ["a"]
    println!("{:?}", str.matches(char::is_numeric).collect::<Vec<&str>>());
    // -> ["1", "2"]
}

正規表現は言語に組み込まれていないため、使用する場合はクレートを導入する必要があります。

replace

指定文字にマッチした文字列を置換する。

mathcesと同様にchar::is***を利用できる。

fn sample() {
    let string: String = String::from("a,b,c,1,2d,e");
    println!("{:?}", string.replace("a", "dummy"));
    // -> "dummy,b,c,1,2d,e"
    println!("{:?}", string.replace(char::is_alphabetic, "dummy"));
    // -> "dummy,dummy,dummy,1,2dummy,dummy"

    let str: &str = "a,b,c,1,2d,e";
    println!("{:?}", str.replace("a", "dummy"));
    // -> "dummy,b,c,1,2d,e"
    println!("{:?}", str.replace(char::is_numeric, "dummy"));
    // -> "a,b,c,dummy,dummyd,e"
}

trim

先頭と末尾の空白を取り除いた文字列を取得する。

fn sample() {
    let string: String = String::from(" abc\t");
    println!("{:?}", string.trim());
    // -> "abc"

    let str: &str = "\r\nabc ";
    println!("{:?}", str.trim());
    // -> "abc"
}

parse

文字列を別の型にパースする。

パースできた場合はOk(T)、パースに失敗した場合はErr(E)を返す。

fn sample() {
    let string: String = String::from("100");
    println!("{:?}", string.parse::<u32>());
    // -> Ok(100)

    let str: &str = "abc";
    println!("{:?}", str.parse::<u32>());
    // -> Err(ParseIntError { kind: InvalidDigit })
}

uppercase / lowercase

文字列を大文字/小文字に変換する。

ascii文字のみ対象とする場合はto_ascii_uppercase,to_ascii_lowercaseを使用する。

ascii文字以外の文字(例えばウムラウト)も対象としたい場合はto_uppercase,to_lowercaseを使用する。

fn sample() {
    let string: String = String::from("aScIi ウムラウトäÖü");
    println!("{:?}", string.to_ascii_uppercase()); // -> "ASCII ウムラウトäÖü"
    println!("{:?}", string.to_ascii_lowercase()); // -> "ascii ウムラウトäÖü"

    let str: &str = "aScIi ウムラウトäÖü";
    println!("{:?}", str.to_lowercase()); // -> "ascii ウムラウトäöü"
    println!("{:?}", str.to_uppercase()); // -> "ASCII ウムラウトÄÖÜ"
}

repeat

同じ文字を複数回繰り返した文字列を返す。

fn sample() {
    let string: String = String::from("abc");
    println!("{:?}", string.repeat(2));
    // -> "abcabc"

    let str: &str = "DEF";
    println!("{:?}", str.repeat(5));
    // -> "DEFDEFDEFDEFDEF"
}

便利なメソッド(String)

次に示すメソッドはString向けのメソッドです。

対象となる変数がmutであることに注意してください。

push

1文字(char)をStringの末尾に追加する。

fn sample() {
    let mut string: String = String::from("a");
    println!("{:?}", string);
    // -> "a"

    string.push('b');
    println!("{:?}", string);
    // -> "ab"
}

push_str

文字列スライス(&str)をStringの末尾に追加する。

fn sample() {
    let mut string: String = String::from("a");
    println!("{:?}", string);
    // -> "a"

    string.push_str("cde");
    println!("{:?}", string);
    // -> "acde"
}

clear

Stringの中身を空にする。

fn sample() {
    let mut string: String = String::from("abcde");
    println!("{:?}", string);
    // -> "abcde"

    string.clear();
    println!("{:?}", string);
    // -> ""
}

マクロ

format!

複数文字の連結やテンプレート文字列に変数を埋め込みたい場合にはformat!マクロを使用すると便利。

JavaScriptをやったことがある方はテンプレートリテラルと同じ認識で基本的にはOK。

fn sample() {
    let name: String = String::from("Rust 太郎");
    let welcome: String = format!("ようこそ {}!!", name);
    println!("{}", welcome);
    // -> ようこそ Rust 太郎!!

    let id = 123;
    let message: String = format!("ID: {}", id);
    println!("{}", message);
    // -> "ID: 123"
}
参考リンク

最後に

文字列スライスという概念がRustならではのものです。

ただ単純に文字を扱う上ではそこまで気にする必要のないものですが、スライスについて理解を深めるとより良い方法で文字列を扱うことができます。

せっかくのRustプログラミングですので、細かい部分の理解を深めて安全安心なプログラミングができるように勉強していこうと思います。

よかったらシェアしてね!
  • URLをコピーしました!
目次