一致する正規表現ですが、Pythonでパターンの一部のみを置き換えます

Aug 17 2020

スタックオーバーフローで見つけたすべての例は、リバースエンジニアリングするには複雑すぎます。

このおもちゃの例を考えてみましょう

s = "asdfasd a_b dsfd"

が欲しいです s = "asdfasd a'b dsfd"

つまり、アンダースコアで区切られた2つの文字を見つけて、そのアンダースコアをアポストロフィに置き換えます。

試み:

re.sub("[a-z](_)[a-z]","'",s)
# "asdfasd ' dsfd"

()はこの問題を解決することになっていると思いましたか?

さらに紛らわしいのは、置き換えたい文字が正常に見つかったように見えるという事実です。

re.findall("[a-z](_)[a-z]",s)
#['_']

なぜこれが置き換えられないのですか?

ありがとう

回答

3 Marat Aug 17 2020 at 00:27

先読みパターンと後読みパターンを使用します。

re.sub("(?<=[a-z])_(?=[a-z])","'",s)

前方/後方のパターンの幅はゼロであるため、何も置き換えません。

UPD:

  • 問題はre.sub、前後の文字を含む、一致した式全体が置き換えられることでした。
  • re.findallまだ式全体と一致していましたが、グループ(内部の括弧)もありました。試合全体はまだだったa_b
  • 先読み/後読み式は、検索の前/後にパターンがあることを確認しますが、一致には含めません。
  • 別のオプションは、いくつかのグループを作成し、それらのグループを置換に入れることでした。 re.sub("([a-z])_([a-z])", r"\1'\2", s)
2 RyszardCzech Aug 17 2020 at 02:48

を使用する場合re.sub、保持するテキストをキャプチャする必要がありますが、削除するテキストはキャプチャしないでください。

使用する

re.sub(r"([a-z])_(?=[a-z])",r"\1'",s)

証明を参照してください。

説明

NODE                     EXPLANATION
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  _                        '_'
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of look-ahead

Pythonコード:

import re
s = "asdfasd a_b dsfd"
print(re.sub(r"([a-z])_(?=[a-z])",r"\1'",s))

出力:

asdfasd a'b dsfd
ShinNShirley Sep 30 2020 at 14:33

re.subそれがマッチしたすべてのものに置き換えられます。

問題を解決するためのより一般的な方法があり、正規表現を再変更する必要はありません。

以下のコード:

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0)) if _reg else r

# 
re.sub(reg,repl, s)

出力: 'Data: year=am_replace_str, monthday=am_replace_str, month=am_replace_str, some other text'

もちろん、ケースにグループが含まれていない場合、コードは次のようになります。

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0))

# 
re.sub(reg,repl, s)