regex per abbinare, ma sostituire solo una parte del pattern, in python

Aug 17 2020

Tutti gli esempi che ho trovato sullo stack overflow sono troppo complicati per me da decodificare .

Considera questo esempio di giocattolo

s = "asdfasd a_b dsfd"

Voglio s = "asdfasd a'b dsfd"

Ovvero: trova due caratteri separati da un trattino basso e sostituiscilo con un apostrofo

Tentativo:

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

Pensavo che ()dovessero risolvere questo problema?

Ancora più confuso è il fatto che sembra che abbiamo trovato con successo il personaggio che vogliamo sostituire:

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

perché questo non viene sostituito?

Grazie

Risposte

3 Marat Aug 17 2020 at 00:27

Usa modelli di sguardo in avanti e indietro:

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

I motivi Guarda avanti / dietro hanno larghezza zero e quindi non sostituiscono nulla.

UPD:

  • Il problema era che re.subsostituirà l'intera espressione corrispondente, inclusa la lettera precedente e quella successiva.
  • re.findallera ancora corrispondente all'intera espressione, ma aveva anche un gruppo (la parentesi all'interno), che hai osservato. L'intera partita era fermaa_b
  • Le espressioni lookahead / lookbehind verificano che la ricerca sia preceduta / seguita da un modello, ma non la includono nella corrispondenza.
  • un'altra opzione era creare diversi gruppi e inserirli nella sostituzione: re.sub("([a-z])_([a-z])", r"\1'\2", s)
2 RyszardCzech Aug 17 2020 at 02:48

Quando si utilizza re.sub, il testo da conservare deve essere catturato, il testo da rimuovere no.

Uso

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

Vedi la prova .

SPIEGAZIONE

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

Codice Python :

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

Produzione:

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

Il re.subsostituirà tutto ciò che ha trovato.

C'è un modo più generale per risolvere il tuo problema e non è necessario modificare nuovamente la tua espressione regolare.

Codice di seguito:

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)

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

Ovviamente, se il tuo caso non contiene gruppi, il tuo codice potrebbe essere questo:

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)