グルーピング正規表現


ある正規表現を用いたpatternとtextを入力として、出力を次の例のように得たいです。つまり、patterにマッチしたテキストをそのまま出力するのではなく、patternの位置にある括弧をつけて出力として得たいです。textとpatternを引数に持ちこのような出力を行う関数を作りたいです。  
pattern: `'(A)_(B|D)([0-9]+).*([a-z])'` text: `'A_B101_c1'` \--> output:
`'(A)_(B)(101)_(c)1'`

pattern: `'([A-Z])12(b|d)(.*)c([1-9]+)'` text: `'A12ba_Bc123'` \--> output:
`'(A)12(b)(a_B)c(123)'`




> これを一般化してどのようなpatternにも適用可能にすることはできますか？

※ 入れ子(nested)になっている正規表現には対応していません

[code]

    import re
    
    def replace_matched_strings(m):
        if m is None: return
        b, e, s = *m.span(0), []
        span = [m.span(i) for i in range(1, len(m.groups())+1)
                if m.group(i) is not None]
        for i, j in span:
            s.append(text[b:i])
            if i < j: s.append(f'({text[i:j]})')
            b = j
        if b < e: s.append(text[b:e])
        return ''.join(s)
                
    pattern = r'(A)_(B|D)([0-9]+).*([a-z])'
    text = 'A_B101_c1'
    print(re.sub(pattern, replace_matched_strings, text))
    
    pattern = r'([A-Z])12(b|d)(.*)c([1-9]+)'
    text ='A12ba_Bc123'
    print(re.sub(pattern, replace_matched_strings, text))
    
    # (A)_(B)(101)_(c)1
    # (A)12(b)(a_B)c(123)
    
[/code]




正規表現で `( )` カッコでくくったパターンにマッチした文字列は、「後方参照」という仕組みで後から参照することができます。  
文字列の置換時に参照するには `\1` の形式で使用します。`()` の数に応じて `\2`, `\3` のように増えていきます。

単純な例として、「マッチしたパターンを `()` でくくりたい」なら、置換後の文字列として `(\1)` を指定すればよいです。

**re.sub の書式:**

[code]

    re.sub（正規表現, “置換する文字列”, 置換対象の文字列）
    
[/code]

念のため補足すると、"置換する文字列" の部分では `()` などの記号は記号のまま扱われます (ここで指定するのは正規表現ではありません)。

質問の一例目に当てはめると以下のような指定になりそうです。

[code]

    re.sub(r'(A)_(B|D)([0-9]+).*([a-z])', r'(\1)_(\2)(\3)_(\4)', text)
    
[/code]

参考:  
[【Python入門】正規表現による文字列置換をマスターしよう｜re.sub](https://www.sejuku.net/blog/23421)

