regex para corresponder, mas apenas substituir parte do padrão, em python

Aug 17 2020

Todos os exemplos que encontrei sobre estouro de pilha são muito complicados para fazer engenharia reversa .

Considere este exemplo de brinquedo

s = "asdfasd a_b dsfd"

eu quero s = "asdfasd a'b dsfd"

Ou seja: encontre dois caracteres separados por um sublinhado e substitua esse sublinhado por um apóstrofo

Tentativa:

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

Achei que ()deveriam resolver esse problema.

Ainda mais confuso é o fato de que parece que encontramos o caractere que queremos substituir:

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

por que isso não é substituído?

obrigado

Respostas

3 Marat Aug 17 2020 at 00:27

Use os padrões de olhar para frente e para trás:

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

Os padrões de olhar para frente / para trás têm largura zero e, portanto, não substituem nada.

UPD:

  • O problema era que re.subsubstituiria toda a expressão correspondida, incluindo a letra anterior e a seguinte.
  • re.findallainda estava combinando com a expressão inteira, mas também tinha um grupo (o parêntese dentro), que você observou. A partida toda ainda estavaa_b
  • As expressões lookahead / lookbehind verificam se a pesquisa é precedida / seguida por um padrão, mas não o inclui na correspondência.
  • outra opção era criar vários grupos e colocá-los na substituição: re.sub("([a-z])_([a-z])", r"\1'\2", s)
2 RyszardCzech Aug 17 2020 at 02:48

Ao usar re.sub, o texto a ser mantido deve ser capturado, o texto a ser removido não.

Usar

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

Veja a prova .

EXPLICAÇÃO

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

Código Python :

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

Resultado:

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

O re.subsubstituirá tudo o que correspondeu.

Existe uma maneira mais geral de resolver seu problema e você não precisa modificar novamente sua expressão regular.

Código abaixo:

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)

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

Claro, se seu caso não contiver grupos, seu código pode ser assim:

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)