регулярное выражение для соответствия, но заменяет только часть шаблона в python

Aug 17 2020

Все примеры переполнения стека, которые я нашел , слишком сложны для реинжиниринга .

Рассмотрим этот игрушечный пример

s = "asdfasd a_b dsfd"

я хочу s = "asdfasd a'b dsfd"

То есть: найдите два символа, разделенных подчеркиванием, и замените это подчеркивание апострофом

Попытка:

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
  • Выражения lookahead / lookbehind проверяют, что поиску предшествует / следует шаблон, но не включают его в соответствие.
  • Другой вариант заключался в создании нескольких групп и замене этих групп: 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)