System.Text.RegularExpressions.GroupsCollection の格納順序
この格納順序がどのようになっているか、ご存知の方はいますか?
即座に「左/内側優先」と思った人は、私同様に CaptureCollection と勘違いされています。(この左内優先は「括弧を開いた順」というほうが解かりやすい気がする)
いくつかの正規表現を記述して簡単にテストしたところ、
- 匿名のグループは、名前付きのグループより前に配置される*1
- 名前付きのぐループは、匿名のグループより後ろに配置される
- 匿名のグループ内は、左/内側優先で配置される
- 名前付きのグループは、左/内側優先で配置されているように見える
- しかし、付与した名前によって順番が入れ替わることがある
たいして多数のパターンをテストしてみたわけではないのですが、GroupCollection では必ず匿名のグループから並び、その並びは CaptureCollection と同様に左/内側優先になっているようです。index 0 は "0" という名前のマッチング全体を表現する特殊グループで固定なので、その影響かもしれません。*2 グループは、キャプチャとは異なり、量指定子によって数が増減しないのですべてのグループが匿名であれば、正規表現とグループのインデックスの対応は容易に想像することができます。
問題は、名前付きのグループの並べルールがよくわからん、ということです。名前でアクセスすれば問題ないのでどうでもいいことかもしれませんが。
やりたかったことは、
(?
pattern (subpattern) pattern)
みたいな正規表現で、name1 にマッチしたときに、その内側にあるグループのキャプチャ文字列を取得したかったんです。文章処理で正規表現文字列をプラグインできる構成にすると、名前の重複がありうるのでプラグイン側でグループ名を付与すると、正規表現全体がコンパイルエラーになりうるんですよね。
IRegexProvider[] providers = ...; StringBuilder sb = new StringBuilder(); // すべての IRegexProvider が提供する正規表現を繋ぐ foreach (Indexed<IRegexProvider>.Item provider in new Indexed<IRegexProvider>(providers)) { sb.AppendFormat("|(<?id{0}>{1})", provider.Index, provider.Expression); } Regex regex = new Regex(sb.ToString()); : : Match m = regex.Match(text); for (int i = 0; i < providers.Length; i++) { int n = GroupNumberFromName("id" + i); if (m.Group[n].Success) { providers[i].MatchCallback(m, n); } }
ってなかんじのイメージです。