文字列を指定文字列を含む形で分割
やりたいこと
指定文字列をTargetとした時
Targetx⇒{ Target, x }
xTarget⇒{ x, Target }
のような配列を作成する。
切り出しの開始位置を求める
まずは単純にするために指定文字列をAとして
分割する文字列をxAxxAとする。
指定文字列の開始位置はString.IndexOf("A", 開始位置);で検索する。
処理順は以下のように考えられる。
- 先頭から指定文字列を検索する。
- 見つかった文字列の次から検索する。(繰り返し)
探索開始位置をstartPositionとすると、上記の処理は以下のようになる。
- startPosition = 0;
- startPosition = String.IndexOf("A", startPosition) + 指定文字列の長さ;
次に終了条件(OR)を考える。
- 対象の文字列には指定文字列がない
- 最後まで探索した
String.IndexOfで指定文字列が存在しない場合は-1を返す。
以上より、終了条件は以下のいずれかになる。
- String.IndexOf("A", startPosition) == -1
- startPostion == 文字列の長さ
以上よりコードは以下のようになる。
public static List<int> GetStrPatternStartIndexes(string str, string pattern)
{
var ret = new List<int>();
var strLen = str.Length;
var patternLen = pattern.Length;
var startPosition = 0; // 先頭から指定文字列を検索
while(startPosition < strLen) // 最後まで検索するまで
{
var index = str.IndexOf(pattern, startPosition);
if (index == -1) break; // 見つからない
ret.Add(index);
startPosition = index + patternLen; // 見つかった文字列の次から検索
}
return ret;
}
実際に分割する
先ほどの例より、分割対象の開始位置は分かった。
切り出しには、String.Substring(開始位置, 文字数);を使用する。
しかし、このままでは以下のように、対象文字列の前後の文字は含まれない。
xAxxAx⇒{ A, A }
対象文字列より前にある文字
先ほどのコードでいうとstartPositionとindexが異なる場合は、
startPotionから"indexから(-)startPotionまで"を切り出す必要がある。
最後尾の対象文字列以降の文字
上の処理を実行しても、2つ目のAのあとの5つ目のxを切り出せない。
つまり最後に見つからないときに、startPositionからすべて切り出す必要がある。
以上を考慮すると、以下の通りになる。
public static List<string> SplitStrByPattern(string str, string pattern)
{
var ret = new List<string>();
var strLen = str.Length;
var patternLen = pattern.Length;
var startPosition = 0; // 先頭から指定文字列を検索
while(startPosition < strLen) // 最後まで検索するまで
{
var index = str.IndexOf(pattern, startPosition);
if (index == -1)// 見つからない
{
ret.Add(str.Substring(startPosition)); // 末尾の文字列
break;
}
if (index != startPosition)
ret.Add(str.Substring(startPosition, index - startPostion));
ret.Add(str.Substring(index, patternLength));
startPosition = index + patternLen; // 見つかった文字列の次から検索
}
return ret;
}