Заменить Regex (в Python) - более простой способ? - PullRequest
43 голосов
/ 29 января 2009

Каждый раз, когда я хочу заменить фрагмент текста, который является частью большего фрагмента текста, я всегда должен делать что-то вроде:

"(?P<start>some_pattern)(?P<replace>foo)(?P<end>end)"

И затем объединить группу start с новыми данными для replace, а затем группу end.

Есть ли лучший способ для этого?

Ответы [ 4 ]

106 голосов
/ 29 января 2009
>>> import re
>>> s = "start foo end"
>>> s = re.sub("foo", "replaced", s)
>>> s
'start replaced end'
>>> s = re.sub("(?<= )(.+)(?= )", lambda m: "can use a callable for the %s text too" % m.group(1), s)
>>> s
'start can use a callable for the replaced text too end'
>>> help(re.sub)
Help on function sub in module re:

sub(pattern, repl, string, count=0)
    Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a callable, it's passed the match object and must return
    a replacement string to be used.
18 голосов
/ 29 января 2009

Посмотрите в документации по Python re для lookaheads (?=...) и lookbehinds (?<=...) - Я почти уверен, что вы этого хотите. Они соответствуют строкам, но не "потребляют" биты совпадающих строк.

11 голосов
/ 29 января 2009

Короткая версия заключается в том, что вы не можете использовать шаблоны переменной ширины в видовых окнах с помощью модуля Python re. Нет способа изменить это:

>>> import re
>>> re.sub("(?<=foo)bar(?=baz)", "quux", "foobarbaz")
'fooquuxbaz'
>>> re.sub("(?<=fo+)bar(?=baz)", "quux", "foobarbaz")

Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    re.sub("(?<=fo+)bar(?=baz)", "quux", string)
  File "C:\Development\Python25\lib\re.py", line 150, in sub
    return _compile(pattern, 0).sub(repl, string, count)
  File "C:\Development\Python25\lib\re.py", line 241, in _compile
    raise error, v # invalid expression
error: look-behind requires fixed-width pattern

Это означает, что вам нужно обойти это, самое простое решение очень похоже на то, что вы делаете сейчас:

>>> re.sub("(fo+)bar(?=baz)", "\\1quux", "foobarbaz")
'fooquuxbaz'
>>>
>>> # If you need to turn this into a callable function:
>>> def replace(start, replace, end, replacement, search):
        return re.sub("(" + re.escape(start) + ")" + re.escape(replace) + "(?=" + re.escape + ")", "\\1" + re.escape(replacement), search)

Это не имеет элегантности решения для поиска за спиной, но это все еще очень четкая, простая однострочная. И если вы посмотрите на то, что эксперт должен сказать по этому вопросу (он говорит о JavaScript, в котором полностью отсутствуют взгляды, но многие принципы совпадают), вы увидите, что его простейшее решение выглядит очень похоже на это.

4 голосов
/ 13 января 2010

Я считаю, что лучшая идея - просто собрать в группе то, что вы хотите заменить, а затем заменить ее, используя начальные и конечные свойства захваченной группы.

привет

Адриан

#the pattern will contain the expression we want to replace as the first group
pat = "word1\s(.*)\sword2"   
test = "word1 will never be a word2"
repl = "replace"

import re
m = re.search(pat,test)

if m and m.groups() > 0:
    line = test[:m.start(1)] + repl + test[m.end(1):]
    print line
else:
    print "the pattern didn't capture any text"

Это напечатает: 'слово1 никогда не будет словом2'

Заменяемая группа может находиться в любом месте строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...