regex pour correspondre, mais ne remplacer qu'une partie du modèle, en python

Aug 17 2020

Tous les exemples que j'ai trouvés sur le débordement de pile sont trop compliqués pour que je puisse faire de l'ingénierie inverse .

Considérez cet exemple de jouet

s = "asdfasd a_b dsfd"

Je voudrais s = "asdfasd a'b dsfd"

C'est-à-dire: trouver deux caractères séparés par un trait de soulignement et remplacer ce trait de soulignement par une apostrophe

Tentative:

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

Je pensais qu'ils ()étaient censés résoudre ce problème?

Encore plus déroutant est le fait qu'il semble que nous ayons réussi à trouver le personnage que nous voulons remplacer:

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

pourquoi cela n'est-il pas remplacé?

Merci

Réponses

3 Marat Aug 17 2020 at 00:27

Utilisez des modèles d'anticipation et d'anticipation:

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

Les motifs à regarder devant / derrière ont une largeur nulle et ne remplacent donc rien.

UPD:

  • Le problème était que re.subcela remplacera toute l'expression correspondante, y compris la lettre précédente et la lettre suivante.
  • re.findallcorrespondait toujours à l'expression entière, mais il y avait aussi un groupe (la parenthèse à l'intérieur), que vous avez observé. Tout le match était encorea_b
  • Les expressions lookahead / lookbehind vérifient que la recherche est précédée / suivie d'un modèle, mais ne l'incluent pas dans la correspondance.
  • une autre option était de créer plusieurs groupes et de placer ces groupes dans le remplacement: re.sub("([a-z])_([a-z])", r"\1'\2", s)
2 RyszardCzech Aug 17 2020 at 02:48

Lors de l'utilisation re.sub, le texte à conserver doit être capturé, le texte à supprimer ne doit pas.

Utilisation

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

Voir la preuve .

EXPLICATION

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

Code Python :

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

Production:

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

Le re.subremplacera tout ce qu'il correspond.

Il existe une manière plus générale de résoudre votre problème et vous n'avez pas besoin de modifier à nouveau votre expression régulière.

Code ci-dessous:

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)

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

Bien sûr, si votre cas ne contient pas de groupes, votre code peut ressembler à ceci:

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)