質問: inData.Substring(index + delimiter.Length, inData.Length - (index + delimiter.Length)); return true; } ``` 1. この関数は「○○課 山田 太郎」のような文字列を区切る為に使用していた 2. 最初は上記のコードを使って区切っていた(そもそもindexOfは「最初に見つけた部分のindexを返す関数」のはずなのに何故か最後の部分のindexを返してくれる。未だに謎です。) 3. 「1.」の時はデリミターに「課」を入力させ、別の関数で「前にくっつける」ことを行い、「○○課」「山田 太郎」と区切っていた 4. 後になって「○○課\r\n山田 太郎」や「○○課○○部 山田太郎」というのが出てきた 5. indexOfに"\r\n"や"(課|部)"を入れても区切ってくれない 6. 「区切文字がマッチする箇所が複数存在したら一番後ろで区切る」という仕様が決められている 7. 質問に至る
回答: `LastIndexOf`のように検索したいのか文字数で判断したいのかよくわかりませんが、`Regex.Matches`を呼び出して最長または最後`Match`を採用すればよいのでは。 ``` var ms = Regex.Matches(inData, delimiter); if (ms.Count > 0) { var m = ms[mc.Count -1]; front = inData.Substring(0, m.Index); delimiter = m.Value; behind = inData.Substring(m.Index + m.Length); return true; } ``` 正規表現で否定先読みを使用すべきではない理由 正規表現を複数の箇所にマッチさせる場合、特定の箇所にマッチしたかどうか自体が後続の結果に影響します。ですので正規表現自体を書き換えるのは避けるべきです。 ### 例 文字列`aaba`にパターン`(a+|aba)`をマッチさせた場合、該当しうる箇所は 1. 1 文字目: `a` 2. 1-2文字目: `aa` 3. 2 文字目: `a` 4. 2-4文字目: `aba` 5. 4 文字目: `a` です。しかし`Regex.Matches`でマッチさせた場合、結果として`aa`(2.)と`a`(5.)が得られます。 これは`aa`に含まれる1.と、`aa`の途中から始まる3.と4.が除外されたからです。 しかし否定先読みで`(a+|aba)(?!.*(a+|aba))`とパターンを変えると、1.と2.は4文字目の`a`のためにマッチしません。ですので2文字目から始まる3.と4.が比較され、より長い`aba`がマッチします。 もちろん「`a`も`aba`も終了位置は同じ」と開き直ることもできますが、`GetSplitData`メソッドの実装を知らない人になぜ`a`ではなく`aba`がデリミターなのかという仕様を説明することは難しいでしょう。 単純に`Regex.Matches`を使用すれば正規表現の基本ルールにのっとっていますので、第三者へのわかりやすさからこちらをお勧めします。 例2 否定先読みを使うと問題になるもっとわかりやすいケースがあったので結果だけ掲載しておきます。原因は上で説明した通りです。 * `Regex.Split("a==b==c", "==")` -> `"a", "b", "c"` * `Regex.Split("a====c", "==")` -> `"a", "", "c"` * `Regex.Split("a====c", "==(?!.*==)")` -> `"a=", "=c"`