regex pour correspondre, mais ne remplacer qu'une partie du modèle, en python
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
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.sub
cela remplacera toute l'expression correspondante, y compris la lettre précédente et la lettre suivante. re.findall
correspondait 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)
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
Le re.sub
remplacera 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)