読者です 読者をやめる 読者になる 読者になる

無粋な日々に

日々の技術メモ(データ分析界隈)

Python: 正規表現で複数行マッチングの置換を行う

Python正規表現で複数行マッチングの置換を行う

Pythonreモジュールを使って複数行に渡る正規表現マッチング・置換を行う場合はflagsオプションに適宜re.MULTILINEre.DOTALLを指定する。

# 直接置換する場合
re.sub(pattern, repl, string, flags=(re.MULTILINE | re.DOTALL))

# compileする場合
r = re.compile(pattern, flags=(re.MULTILINE | re.DOTALL))
re.sub(pattern, repl, string)

具体的な内容

^$を各行にマッチさせるre.MULTILINEフラグ指定

re.MULTILINEを指定しない場合、^は文字列の先頭に、$は文字列の末尾しかマッチしない。つまり1つの文字列変数につきそれぞれ1箇所しかマッチし得ない。なので改行を含む文字列で各行の 先頭や末尾(改行)にマッチさせたい場合はre.MULTILINEを指定する必要がある

例えば、以下の文字列(SQL)のコメント行を2行とも削除したい場合

# この文字列のコメント部分を削除したい
query = """\
-- this is 1st comment
-- this is 2nd comment
select * from table limit 10;
"""

re.MULITILINEフラグ指定がないと1行しか削除されません

# re.MULITILINE指定なしで削除
re.sub(r'^---*.*', '', query)

#>-- this is 2nd comment
#>select * from table limit 10;

re.MULITILINEフラグ指定を行うと2行とも削除されます

# re.MULITILINE指定して削除
re.sub(r'^\s*---*.*$', '', query, flags=re.MULTILINE)

#>select * from table limit 10;

ちなみにre.MULITILINEフラグ指定なしで正規表現$をパターンに入れると何も削除されません。マッチないからです。フラグ指定なしでは$文字列の末尾にしかマッチしません。行の末尾にはマッチしません。

# re.MULITILINE指定なしで、$を入れる
re.sub(r'^---*.*$', '', query)

#>-- this is 1st comment
#>-- this is 2nd comment
#>select * from table limit 10;

re.MULTILINE(原文) 指定されると、パターン文字 '^' は、文字列の先頭および各行の先頭(各改行の直後)とマッチします;そしてパターン文字 '$' は文字列の末尾および各行の末尾 (改行の直前) とマッチします。デフォルトでは、 '^' は、文字列の先頭とだけマッチし、 '$' は、文字列の末尾および文字列の末尾の改行の直前(がもしあれば)とマッチします。6.2. re — 正規表現操作

.を改行(\n)にもマッチさせるre.DOTALLフラグ指定

re.DOTALLフラグ指定が無いと.は改行にはマッチしません。re.DOTALLフラグ指定をすることで改行にもマッチします。

同様に以下の文字列(SQL)の/* ~ */コメント行のすべてを削除したい場合

query = """\
/*
this is 1st comment
this is 2nd comment
*/
select * from table limit 10;
"""

フラグ指定がないと削除されません

# re.MULITILINE指定なし
re.sub(r'/\*.*\*/', '', query)

#>/*
#>this is 1st comment
#>this is 2nd comment
#>*/
#>select * from table limit 10;

re.DOTALLフラグ指定を行うと4行とも削除されます

# re.MULITILINE指定
re.sub(r'/\*.*\*/', '', query, flags=re.DOTALL)

#>select * from table limit 10;

※ちなみにこれでコメント削除されるんですが、実際に上記のパターンをこのまま使うと、/*~*/が2箇所以上出てきた場合、コメントブロックに挟まれた途中のコードも全部消えるので注意が必要です(greedy matching; どう書くのが良いんでしょう。教えてください)

re.DOTALL¶(原文) 特殊文字 '.' を、改行を含む任意の文字と、とにかくマッチさせます;このフラグがなければ、 '.' は、改行 以外の 任意の文字とマッチします。6.2. re — 正規表現操作

フラグを両方指定する場合

フラグなのでORを取れば大丈夫です。

flags = (re.MULITILINE | re.DOTALL)