ヒットした位置に1を足した位置から検索したからといって、直後の検索対象がヒットすると思ったか？

# JavaScript における開始位置を指定した文字列検索

JavaScript では、文字列内の位置は 0-origin で指定する。
[`indexOf()`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf) 関数では、第2引数で検索を開始する位置を指定できる。

```javascript:JavaScript で開始位置を指定した文字列検索をするサンプル
const data = "宇宙パトロールルル子";
let pos = 0;
while (pos >= 0) {
  pos = data.indexOf("ル", pos);
  if (pos >= 0) {
    console.log(`${pos + 1}文字目`);
    pos++;
  }
}
```

このプログラムを[実行する](https://wandbox.org/permlink/6BXzJMQgnTgFV8Xi)と、以下の出力が得られる。

```text
7文字目
8文字目
9文字目
```

ヒットした位置に1を足した位置から検索することで、直後の検索対象を見つけることができている。

# なでしこ における開始位置を指定した文字列検索

[なでしこ](https://nadesi.com/) で[文字列を扱う命令](https://nadesi.com/v3/doc/index.php?plugin_system%2F%E6%96%87%E5%AD%97%E5%88%97%E5%87%A6%E7%90%86&show)の多くは、位置を 1-origin で指定するようである。
「[文字検索](https://nadesi.com/v3/doc/index.php?plugin_system%2F%E6%96%87%E5%AD%97%E6%A4%9C%E7%B4%A2&show)」命令を用いることで、文字列内の指定した位置から対象の文字列を検索することができる。

```text:なでしこ で開始位置を指定した文字列検索をするサンプル
データは「宇宙パトロールルル子」。
位置は1。
位置が0超の間繰り返す
  位置はデータで位置から「ル」を文字検索。
  もし、位置が0超ならば
    「{位置}文字目」と表示。
    位置を1だけ増やす。
  ここまで。
ここまで。
```

このプログラムを v3.6.45 の[簡易エディタ](https://nadesi.com/doc3/index.php?%E3%81%AA%E3%81%A7%E3%81%97%E3%81%933%E7%B0%A1%E6%98%93%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF)で実行すると、以下の出力が得られる。

```text
7文字目
9文字目
```

0-origin と 1-origin の読み替えを行っただけで、JavaScript 版と同様の処理をしているはずなのに、なぜか「8文字目」が抜けてしまっている。

実は、文字列を扱う多くの命令は位置を 1-origin で指定しているが、この[「文字検索」命令](https://github.com/kujirahand/nadesiko3core/blob/7c0f7233a0a706711a6feb024c219fb03fffd607/src/plugin_system.mts#L1136)の検索開始位置は、**なぜか 0-origin で指定する**のである。

[マニュアル](https://nadesi.com/v3/doc/index.php?plugin_system%2F%E6%96%87%E5%AD%97%E6%A4%9C%E7%B4%A2&show)をよく見ると、

> ```
> 「しんぶんし」で4から「ん」を文字検索。
> ```

の結果が `0` (見つからない) であることや、「再帰で全て検索する例」の例で検索を `0` から始めていることで、検索開始位置が 0-origin であることを匂わせている。

匂わせじゃなくてはっきり 0-origin だって書けばいいのに…… ([例のロボット](https://dic.pixiv.net/a/%E3%83%AD%E3%83%9C%E3%83%83%E3%83%88%28%E5%AD%A6%E3%81%B3%E8%80%83%E3%81%88%E3%82%8B%E6%AD%B4%E5%8F%B2%29))

# プロデル における開始位置を指定した文字列検索

[文字数の数え方](https://qiita.com/mikecat_mixc/items/6afad71b0f38e7271a23)については **なでしこ と違って**一貫性がありそうだった [プロデル](https://produ.irelang.jp/) でも、同様に検索をしてみよう。
プロデルでも、文字列中の位置の指定は 1-origin が基本のようである。
「[探す](https://produ.irelang.jp/docs/primitive/string.htm#%E6%8E%A2%E3%81%99)」手順を用いることで、文字列内の指定した位置から対象の文字列を検索することができる。

```text:プロデル で開始位置を指定した文字列検索をするサンプル
データは「宇宙パトロールルル子」
位置は1
位置が0より大きい間、繰り返す
  データの(位置)文字目から「ル」を探して、位置とする
  位置が0より大きいなら
    「[位置]文字目」を出力して改行する
    位置を位置+1とする
  そして
そして
```

このプログラムをバージョン 1.9.1273 の[スミレ畑](https://sum.irelang.jp/play/2.0/)で実行すると、以下の出力が得られる。

```text
7文字目
9文字目
```

なんと、プロデルでも **「8文字目」が抜けてしまった。**
プロデルでも検索開始位置の指定は**まさかの 0-origin、一貫性なし**ということである。

同じ「文字目」という表現を使う手順でも、例えば「[取り出す](https://produ.irelang.jp/docs/primitive/string.htm#%E5%8F%96%E3%82%8A%E5%87%BA%E3%81%99)」では 1-origin で位置を指定している。

さらに、プロデルの「探す」のサンプルを見てみると

> ```text
> 内容は「にほんごぷろぐらみんぐげんご ぷろでる」
> 内容の5文字目から「ん」を探して、文字位置とする
> ```

となっている。
この文字列の「5文字目」からの検索では、0-origin でも 1-origin でも結果は変わらない。
なでしこ と違って、0-origin であることを**匂わせてもいない**のである。
もちろん、0-origin であることを**明示もしていない。**

文字数の数え方については なでしこ よりも一貫性があって良さそうだった プロデル だが、文字列検索の開始位置の指定については なでしこ と同様の罠 (他の多くの文字列操作命令が 1-origin を用いる中、0-origin を採用している) があり、その上 なでしこ よりもさらにこのような罠がある (0-origin である) ことをわかりにくくしており、**より悪質**といえるだろう。

[プロデルで始める日本語プログラミング言語入門(#10) 「文字列あれこれ」 - プロデルブログ](https://produ.irelang.jp/blog/2020/08/2175/)
においても、

> 文字列から検索する
>
> 文字列は先頭を1文字目として数えます。

とはっきり書かれており、検索開始位置の指定の特殊さへの言及は見当たらない。

ちなみに、「[前方向へ探す](https://produ.irelang.jp/docs/primitive/string.htm#%E5%89%8D%E6%96%B9%E5%90%91%E3%81%B8%E6%8E%A2%E3%81%99)」手順についても、**検索開始位置の指定は 0-origin、検索結果は 1-origin** のようである。

```text:プロデルの「前方向へ探す」手順を使うサンプル
内容は「宇宙パトロールルル子」
開始位置は8
内容の(開始位置)文字目から「ル」を前方向へ探して、発見位置とする
「[開始位置]文字目から前方向に探して、[発見位置]文字目に見つかりました」を出力する
```

このプログラムをスミレ畑で実行すると、以下の出力が得られた。

```text
8文字目から前方向に探して、9文字目に見つかりました
```

**8文字目**から**前方向**に探しているのに、それより数値が大きい**9文字目**に見つかっているというのは、とっても変な感じ……

# まとめ

JavaScript の `indexOf` による文字列検索では、検索開始位置の指定は他の**文字列操作関数と同じ** 0-origin であり、素直な仕様である。
一方、なでしこの「文字検索」や、プロデルの「探す」「前方向に探す」では、他の文字列操作命令における位置の指定やこの命令の検索結果は 1-origin であるにもかかわらず、検索開始位置の指定は**これらとは異なって** 0-origin である。

0-origin を採用するか 1-origin を採用するかは、言語やライブラリの仕様として決めることであり、どっちが正解というものではない。
しかし、同じ言語やライブラリの中では、統一しておいたほうがわかりやすく、使いやすいだろう。
特に、プロデルではドキュメントから検索開始位置の指定方法が他の文字列操作命令における位置の表現とは異なることを読み取るのは難しそうであり、**かなり不親切だ**といえそうである。