💡

dart 文字列から指定した文字のインデックスを全て取得する。正規表現 allMatches か indexOf

2024/12/12に公開

dartの初心者向け記事

dartで、正規表現の日本語記事を探すと基本的な解説は多数見つかるが、意外にやり方が見つからなかったものがある。それは、文字列から指定した文字のインデックス番号を全て取得する方法である。
 この手の需要は割と多いはずで、他の言語では、関数が普通に用意されている。例えばC#の場合、Regex.Matchesメソッドでは、ヒットした文字とそのインデックス番号がMatchというクラスとして取得できる。では、dartで同じような関数があるのかとさがしてみるもののなかなか見つからない。みなさまも、よろしければ、コメントにてもっとも簡単な書き方や関数をご存じであれば、ご教示ください。
今回は、正規表現のallMachesを使うやり方、indexOfで書いてみるやり方、両方試してみました。
処理の時間は短い文字列だと、indexOfでの自作の方が早いが、長文になれば、「正規表現のallMaches」が速くなった。またコードが短いという意味では「正規表現のallMaches」を使った方が勝っている。

やりたいこと

"dartで文字列のインデックスを、検索して、インデックス番号を、リストで返す方法です。"

という文字列から、

 ["インデ","、","。"]

このリストの文字の位置を全て取得する。
結果としては、

[{9, インデ}, {16, 、}, {21, 、}, {22, インデ}, {31, 、}, {42, 。}]

という感じで取得できるようにします。

正規表現のallMachesでやってみる

List regExpMatches({required String text,required String reserchWordsReg}){
 final reg = RegExp(reserchWordsReg);
 return reg.allMatches(text).map((e) => {e.start,e.group(0)}).toList();
}

indexOf メソッドでやってみる

List matches({required String text,required List<String> reserchWords}){
var indexs = {};
for(int ii = 0; ii < reserchWords.length; ii++){
  int i = 0;
  while(i <text.length){
    var matchIndex = text.indexOf(reserchWords[ii],i);
    if(matchIndex > 0){
      indexs[matchIndex]=reserchWords[ii];
      i =matchIndex;
    }
    i++;
  }      
}
return indexs.entries.toList()..sort((e1, e2) => e1.key.compareTo(e2.key));
}

正規表現のallMachesの方が明らかに短く書けると思う。
だが、indexOfを使った書き方は、もっと効率のいい書き方がるのかも?

ともかく、両者の関数を実行して処理時間を計測してみよう。

void main(){
  String text = "dartで文字列のインデックスを、検索して、インデックス番号を、リストで返す方法です。";
  final stopwatch = Stopwatch();
  stopwatch.start();
  print(matches(text: text, reserchWords: ["インデ","、","。"]));
  stopwatch.stop();
  print('${stopwatch.elapsedMicroseconds} microseconds');
  final stopwatch2 = Stopwatch();
  stopwatch2.start();
  print(regExpMatches(text: text, reserchWordsReg: "インデ|、|。"));
  stopwatch2.stop();
  print('${stopwatch2.elapsedMicroseconds } microseconds');
  }
// 二つの関数を順番にコメントアウトして実行しました。

実行結果

[{9, インデ}, {16, 、}, {21, 、}, {22, インデ}, {31, 、}, {42, 。}]
allMatchesを使用したら、 4419 microsecondsでした。
[MapEntry(9: インデ), MapEntry(16: 、), MapEntry(21: 、), MapEntry(22: インデ), MapEntry(31: 、), MapEntry(42: 。)]
indexOfを使用したら、4146 microsecondsでした。

私のへっぽこな関数の書き方では、今回の試した文章量では、スピードはたいしてかわらないようですね。ただ文字が長ければ長いほど、正規表現allMatchesの方が処理速度が速いです。

Discussion