7

I've been yanking clumps of hair out for 30 minutes doing this one...

I have a dictionary, like so:

{'search': 'replace',
 'foo':    'bar'}

And a string like this:

Foo bar %foo% % search %.

I'd like to replace each variable with it's equivalent text from the dictionary:

Foo bar bar replace.

My current regex fails, so here it is (key and value are from dictionary.items()):

 re.sub(r'%\d+' + key + '[^%]\d+%', value, text)

Any help would be appreciated, as this regex stuff is driving me nuts...

4 Answers 4

8

If you're flexible with your syntax in your string, Python has a built in mechanism for that:

>>> print 'Hello, %(your_name)s, my name is %(my_name)s' % {'your_name': 'Blender', 'my_name': 'Ken'}
Hello, Blender, my name is Ken

Alternatively, if you want that syntax, I'd avoid regular expressions and just do this:

>>> vars = {'search': 'replace',
...  'foo':    'bar'}
>>> mystring = "Foo bar %foo% % search %."
>>> for k, v in vars.items():
...     mystring = mystring.replace('%%%s%%' % k, v)
... 
>>> print mystring
Foo bar bar % search %.
Sign up to request clarification or add additional context in comments.

4 Comments

I didn't know that the %d thing could take variable names. Thanks. I can do a blind search-and-replace, but I'd also like to account for whitespace (which is why I tried the regex). I'll keep fiddling with it...
On second thought, the % will be troublesome. I think I'll go with the parentheses too, as it's much easier to implement and works well. Any way I can omit the d at the end? I'm using this for a template engine, so the simpler the syntax the better.
Your alternative doesn't work to well. It can do undesired double-interpolation. e.g. { 'foo': '%bar%', 'bar': 'baz' }. @murgatroid99's solution isn't affected by this problem.
I ended up using %(this)% syntax, as the percentage signs would be quite hard to deal with and escape when writing. Search and replace > regex.
4

If you want it in one statement, you could do the following (assuming s is the string and d is the dictionary):

re.sub(r"[%]\s*[^%]+\s*[%]",lambda k:d.get(k[1,-1].strip(),k),s)

This uses a function in the replacement part to get each value from the dictionary, and ignores if it is not in the dictionary.

Edit: fixed bug with unwanted whitespace appearing in lookup key

Comments

2

Maybe I'm missing something, but wouldn't the following regex just work?

re.sub(r'%\s?' + key + '\s?%', value, text)

The only thing that's a bit special are the optional spaces; they can be matched with \s?.

Comments

2

Using the replacement function support of re.sub:

def replace(s, kw, pattern=re.compile(r'%\s*(\w+)\s*%')):
    """
    Replace delimited keys in a string with values from kw.

    Any pattern may be used, as long as the first group defines the lookup key.
    """
    lookup = lambda match: kw.get(match.group(1), match.group())
    return pattern.sub(lookup, s)

>>> replace('Foo bar %foo% % search %.', {'search': 'replace', 'foo': 'bar'})
'Foo bar bar replace.'

You can change the lookup function to customize how lookup errors are treated, and so on.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.