43 votes

Remplacement de Regex (en Python) - une méthode plus simple ?

Chaque fois que je veux remplacer un morceau de texte qui fait partie d'un plus grand morceau de texte, je dois toujours faire quelque chose comme :

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

Et ensuite, concaténer les start avec les nouvelles données pour replace et ensuite le end groupe.

Existe-t-il une meilleure méthode pour cela ?

105voto

>>> 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.

18voto

zenazn Points 8373

Regardez dans le Python documentation pour les têtes de turc (?=...) et les regards (?<=...) -- Je suis presque sûr que c'est ce que vous voulez. Ils correspondent à des chaînes de caractères, mais ne "consomment" pas les bits des chaînes de caractères qu'ils correspondent.

11voto

Ben Blank Points 21786

La version courte est que vous ne peut pas utiliser des motifs de largeur variable dans les transparents en utilisant l'outil Python re module. Il n'y a aucun moyen de changer cela :

>>> 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

Cela signifie que vous devrez le contourner, la solution la plus simple étant très similaire à ce que vous faites actuellement :

>>> 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)

Cette solution n'a pas l'élégance de la solution lookbehind, mais elle reste très claire et directe. Et si vous regardez ce que l'avis d'un expert sur la question (il parle de JavaScript, qui est totalement dépourvu de lookbehinds, mais la plupart des principes sont les mêmes), vous verrez que sa solution la plus simple ressemble beaucoup à celle-ci.

4voto

Adrián Deccico Points 885

Je crois que la meilleure idée est de capturer dans un groupe ce que vous voulez remplacer, puis de le remplacer en utilisant les propriétés de début et de fin du groupe capturé.

salutations

Adrián

#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"

Cela imprimera : 'le mot 1 ne sera jamais un mot 2'.

Le groupe à remplacer peut être situé dans n'importe quelle position de la chaîne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X