[Python]正規表現・改行含む複数行マッチング・繰り返しマッチング・複数条件マッチング

Pythonで、掲題通り、改行を含んだ文字列を検索とか置換を行いたくて、その正規表現について調べました。

対象文字列の例

※日本語の文字列です
以下の文字列が、test_textに代入されているケースを想定

あああ
いいい
[start:ううう
えええ
おおお
かかか:end]
ききき
くくく
[start:けけけ
こここ:end]
さささ

[start:~~~:end]の文字列を抽出、置換したかった。

問題点分解

  1. 正規表現による改行をまたいだ文字列の検索
  2. 検索結果が複数あった場合の正確な抽出

結論

以下の正規表現でいけます。

import re

~~~省略~~~
replace_word = "\[start:.*?:end\]"
# 抽出
word_list = re.findall(replace_word, test_text, flags=re.DOTALL)
# 置換
new_text = re.sub(replace_word, "new_word\n", test_text, count=1, flags=re.DOTALL)

word_listは以下の通り

word_list[0] = "[start:ううう\nえええ\nおおお\nかかか:end]"
word_list[1] = "[start:けけけ\nこここ:end]"

new_textは以下の通り

new_text = あああ\nいいい\nnew_word\nききき\nくくく\nnew_word\nさささ

肝となるところ

3つあります。

  • re.findall、re.subの引数
    flags=re.DOTALL

    '.' 特殊文字を、改行を含むあらゆる文字にマッチさせます。このフラグがなければ、'.' は、改行 以外の あらゆる文字とマッチします。インラインフラグの (?s) に相当します。

    参考:re.DOTALL

  • 正規表現
    使った正規表現:.*?
    それぞれの説明:
    .

    (ドット) デフォルトのモードでは改行以外の任意の文字にマッチします。 DOTALL フラグが指定されていれば改行も含む全ての文字にマッチします。

    *

    直前の正規表現を 0 回以上、できるだけ多く繰り返したものにマッチさせる結果の正規表現にします。例えば ab*'a''ab'、または 'a' に任意個数の 'b' を続けたものにマッチします。

    ?

    直前の正規表現を 0 回か 1 回繰り返したものにマッチさせる結果の正規表現にします。例えば ab?'a' あるいは 'ab' にマッチします。

    参考:正規表現のシンタックス

  • re.subの引数
    count=1

    オプション引数 count は出現したパターンを置換する最大の回数です。 count は非負整数です。省略されるか 0 なら、出現した全てが置換されます。パターンへの空マッチは前の空マッチに隣接していないときのみ置換されるので、 sub('x*', '-', 'abxd')'-a-b--d-' を返します。

    参考:re.sub

うまくいかなかったケース

  • 正規表現
    .*?のところを(.|\s)*?としていた
    ※正規表現の\sは余白を示す正規表現。改行コードにもマッチする。

matches = re.findall('(\[start:(.|\s)*?:end\])', test_text)

半角文字のみだった場合は、うまくいきましたが、日本語のように2バイト文字が混ざったケースではうまくいきませんでした。

その他、参考

コメント

タイトルとURLをコピーしました