環境
検証に使用した環境は,以下の通りです.
- Python 3.11.3 (main, Apr 11 2023, 16:14:10) [GCC 10.2.1 20210110] on linux
- rustc 1.68.2 (9eb3afe9e 2023-03-27) on linux
文字列を数える
Python ならば len()
関数を使うだけです.
>>> len("りんご") 3
Rust では,文字列を表現するための&str
や String
などの型があります.len()
関数も存在しますが,長さをバイトで表現するため,UTF8など可変長のエンコードを使用すると直感的ではない値が返ってきます.
println!("{}", String::from("りんご").len()) // 9
Python と Rust で結果が一致するような文字列の長さの取得方法を検討したところ,Python の len()
関数と,Rust の chars().count()
関数がほぼ一致することが分かりました.
Rust
fn count_str(m: &str) -> usize { m.chars().count() } #[test] fn test_count_str() { let cases = vec![ ("Hello", 5), ("こんにちは", 5), ("Newline\r\n", 9), ("a̐éö̲", 7), ("🤣", 1), ]; for (i, o) in cases { assert_eq!(count_str(i), o); } }
Python
import unittest class RustTest(unittest.TestCase): def test_count_str(self) -> None: cases = [ ("Hello", 5), ("こんにちは", 5), ("Newline\r\n", 9), ("a̐éö̲", 7), ("🤣", 1), ] for i, o in cases: with self.subTest(i=i): self.assertEqual(len(i), o)
上記の "a̐éö̲" は一見すると3文字ですが,Pythonでは7文字になります.
>>> list("a̐éö̲") ['a', '̐', 'e', '́', 'o', '̈', '̲']
これは,Unicode 書記素クラスタの例で,例えば unicode_segmentation クレートで,Unicode 書記素クラスタの対応について詳しく記載されております.Pythonはこれを複数の文字として処理するようなので Rust で合わせます.
上記のユニットテストを実行して,ほぼ一致する出力が得られることを確認しました.
なお,Python で UCS-2 と UCS-4 のどちらのモードなのかが問題となる場合がなきにしもあらずらしいですが,linux 環境の Python ならばほぼ UCS-4 なので,上記コードで問題なさそうです.*1
文字列を数えるのは一見するととても簡単そうですが,様々な文字が存在する場合,文字の数をどのように定義するべきか,という問題にたどり着くことが分かりました.汎用的なアプリケーションを作成する場合は,ひとまず UTF8 で処理できるようにしておくのが重要だと思います.