Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compose string from regex pattern with named groups and datadict in python?

Tags:

python

regex

Short version:

I want to crate function which replace all named groups in regular expression with coresponding data from datadict. For example:

Input: expr=r"/(?P<something>\w+)/whatever/(?P<something2>\w+)" data={"something":123, "something2": "thing"}
Output: "/123/whatever/thing"

But i have no idea how to do it.

Some addtional info:

I have code which iterate trough list of tuples containing name and pattern and trying to use re.search. In case that re.search match given string it returns name from current tuple and groupdict() (which is dict with data from re.search).

Here is the code

class UrlResolver():
def __init__(self):
    self.urls = {}

def parse(self, app, url):
    for pattern in self.urls[app]:
        data = re.search(pattern[1], url)
        if data:
            return {"name": pattern[0], "data": data.groupdict()}

Now i would like to create function:

def compose(self, app, name, data):
    for pattern in self.url[app]:
        if pattern[0] == name:
            return string composed from regex expression and data from data dict.

Above function should replace all named groups with coresponding data from datadict.

SOLUTION

Using answer provided by Hans Then (Thanks!) and some other info here is the solution:

    def _group_replacer(data, match):
        data_key = match.group(1)
        return data[data_key]

    expression = r"\([^\(]*<([^<]*)>[^\(]*\)"
    expression = re.compile(expression)

    reversed = re.sub(expression, partial(_group_replacer, data), string)

function "partial" can be imported from functools

like image 466
Zelo Avatar asked Nov 07 '12 11:11

Zelo


2 Answers

Have a look at the re.sub() function. This function can be called with a replacement function as the second parameter. See http://docs.python.org/2/library/re.html

That function you'd have to define yourself. It would have to take a match object as its parameter. In it you should look at the match object, extract the match groups and replace them with the values from the dictionary.

You can extract the text from the string that you do not need to replace from the original string by looping through the groups and calling start, end = span(group) on them.

EDIT

I misread your original question. I see now that you do not wish to replace the matches from the regular expressions, but the regular expressions themselves. In this case the difficult part will be to create a regular expression that matches a named regular expression. My solution still holds, but can be somewhat simpler.

To do proper penance I created the following example.

d = { 'something': 'completely',
      'something2': 'different' }

def repl(m):
    s = m.group(1)
    return d[s]

s = "/(?P<something>\w+)/whatever/(?P<something2>\w+)"
p = re.compile(r'\(\?P<(.*?)>\\w\+\)')

print p.sub(repl, s)

This will print

/completely/whatever/different

like image 197
Hans Then Avatar answered Oct 11 '22 07:10

Hans Then


Using a method demonstrated by F.J here, you could perform the substitution this way:

import re

data = {"something" : 123, "something2" : "thing"}
expr = r"/(?P<something>\w+)/whatever/(?P<something2>\w+)"

def matchsub(match, data):
    result = list(match.string)
    pat = match.re
    # print(pat)
    for key, index in pat.groupindex.items():
        # print(key, index, data[key], match.start(index), match.end(index))
        result[match.start(index):match.end(index)] = str(data[key])
    return ''.join(result)

result = matchsub(re.search(expr, "hi/ABC/whatever/DEF/there"), data)
print(result)

yields

hi/123/whatever/thing/there
like image 37
unutbu Avatar answered Oct 11 '22 08:10

unutbu