LoginSignup
2
0

Rustで文字列を数える

Posted at

文字列を数えたい

プログラムを書いているとある文字列が何文字であるのか数えたくなる瞬間がある。今回はたまたまRustでそのようなシチュエーションに遭遇したとする。

取り合えず以下のようなプログラムを考える

fn main() {
	let value = "ハローワールド";
}

value変数に入っている&strの長さが知りたいとする。
雑にエディタのサジェスチョンを見ると&str型にlen()関数があるのが分かる
というわけでコードを以下のようにしてみる

fn main() {
	let value = "ハローワールド";
	let len = value.len();
	assert_eq!(7, len);
}

直感的には通りそうだがこれは実行時エラーとなる

len

len変数の中身を確かめてみると21となってる。なぜか。
&str.len(&self)のドキュメントを読んでみると以下のようになっている。

selfの長さを返す
ここでいう長さとはバイト数であり、文字数ではない。いいかえれば、人間の考える文字列の長さではない場合がある。
str - Rustより筆者訳

lenは文字列の長さではなくバイト長を返すらしい。
日本語、というかASCIIでない文字はバイト数が1ではないため返り値と文字列の長さが一致しない。emojiなども同様である🤔
実際にハローワールドutf-8に直すと以下のようになり、21バイトである。

\xe3\x83\x8f\xe3\x83\xad\xe3\x83\xbc\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\xab\xe3\x83\x89

では、文字数を数えるにはどうすればよいのか

chars, count

文字数を数えたい場合には一般的に以下のようなコードを書く

fn main() {
	let value = "ハローワールド";
	let len = value.chars().count();
	assert_eq!(7, len);
}

chars()は文字列スライスの、1文字に関するイテレータを返す
str - Rustより筆者訳

count()はイテレータの長さを返すので事実上文字列の長さとなる。
しかしドキュメントにはこのような記載もある。

charはUnicodeのスカラー値を表し、あなたの考える文字とは一致しない可能性があることが重要だ。あなたが実際に欲しいのは、書記素クラスタに対する反復処理かもしれない。この機能はRustの標準では提供されていないのでcrate.ioをチェックしてほしい
str - Rustより筆者訳

書記素クラスタとは

書記素クラスタとは、人間が1文字として認識する最小単位であるらしい。
Unicodeでは以下のように求められると定まっている。
https://unicode.org/reports/tr29/

Rustで書記素クラスタを扱う場合はunicode-segmentationを使用するのが一般的なように見える。(筆者の観測範囲内で)
ドキュメントのexampleによればこのように使用できるようだ

use unicode_segmentation::UnicodeSegmentation;

fn main() {
    let s = "a̐éö̲\r\n";
    let g = s.graphemes(true).collect::<Vec<&str>>();
    let b: &[_] = &["a̐", "é", "ö̲", "\r\n"];
    assert_eq!(g, b);

    let s = "The quick (\"brown\") fox can't jump 32.3 feet, right?";
    let w = s.unicode_words().collect::<Vec<&str>>();
    let b: &[_] = &["The", "quick", "brown", "fox", "can't", "jump", "32.3", "feet", "right"];
    assert_eq!(w, b);

    let s = "The quick (\"brown\")  fox";
    let w = s.split_word_bounds().collect::<Vec<&str>>();
    let b: &[_] = &["The", " ", "quick", " ", "(", "\"", "brown", "\"", ")", "  ", "fox"];
    assert_eq!(w, b);
}

unicode-segmentation - crates.io: Rust Package Registryより引用

まとめ

  • lenはバイトの長さを返す
  • chars().count()は文字数を返す
  • 書記素クラスタを考慮する場合はcrateを使う

参考文献

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0