开发者

Rebuild regex string based on match keywords in python

开发者 https://www.devze.com 2023-02-09 07:14 出处:网络
Example regular expression regex = re.compile(\'^page/(?P<slug>[-\\w]+)/(?P<page_id>[0-9]+)/$\')

Example regular expression

regex = re.compile('^page/(?P<slug>[-\w]+)/(?P<page_id>[0-9]+)/$')
matches = regex.match('page/slug-name/5/')
>> matches.groupdict()
{'slug': 'slug-name', 'page_id': '5'}

Is there an easy way to pass a dict back to the regex to rebuild a string?

i.e. {'slug': 'n开发者_JAVA百科ew-slug', 'page_id': '6'} would yield page/new-slug/6/


Regex methods operate on strings. Since you have a dict, I think the string format method is a better fit:

In [16]: d={'slug': 'new-slug', 'page_id': '6'}

In [17]: 'page/{slug}/{page_id}'.format(**d)
Out[17]: 'page/new-slug/6'

There are all sorts of more complicated regex for which the following won't work, but if you always use non-nested named match groups (?P<name>...) and restrict pat to having nothing more complicated than \A, or ^, \Z or $ or \b in your regex pattern otherwise, then perhaps you could do this:

import re
import string


pat=r'\Apage/(?P<slug>[-\w]+)/(?P<page_id>[0-9]+)/\Z'
regex = re.compile(pat)
matches = regex.match('page/slug-name/5/')
print(matches.groupdict())
# {'page_id': '5', 'slug': 'slug-name'}

# Convert '(?P<slug>...)' to '{slug}'    
reverse_pat=re.sub(r'\(\?P<(.*?)>.*?\)',r'{\1}',pat)
# Strip off the beginning ^ and ending $
reverse_pat=re.sub(r'^(?:\\A|\^)(.*)(?:\\Z|\$)$',r'\1',reverse_pat)
# drop any `\b`s.
reverse_pat=re.sub(r'\\b',r'',reverse_pat)
# there are many more such rules one could conceivably need... 
print(reverse_pat.format(**matches.groupdict()))
# page/slug-name/5/


Here is a solution which does not require a new regex:

import re
import operator

regex = re.compile('^page/(?P<slug>[-\w]+)/(?P<page_id>[0-9]+)/$')
matches = regex.match('page/slug-name/5/')
groupdict = {'slug': 'new-slug', 'page_id': '6'}
prev_index = matches.start(0)
new_string = ""
for group, index in sorted(regex.groupindex.iteritems(), key=operator.itemgetter(1)):
    new_string += matches.string[prev_index:matches.start(index)] + groupdict[group]
    prev_index = matches.end(index)

new_string += matches.string[prev_index:matches.end(0)]
print new_string
# 'page/new-slug/6/'

This works by replacing named groups by the value provided in groupdict, the rest of the string is inserted using slices on the input string (matches.string). new_string will be the portion of the original string that matched the regex with the relevant replacements. To get new_string to include even the non-matching portions of the string replace prev_index = matches.start(0) with prev_index = 0 and remove matches.end(0) from the final slice after the for loop.


Here's a solution using sre_parse

import re
from sre_parse import parse

pattern = r'^page/(?P<slug>[-\w]+)/(?P<page_id>[0-9]+)/$'
regex = re.compile(pattern)
matches = regex.match('page/slug-name/5/')
params = matches.groupdict()
print params
>> {'page_id': '5', 'slug': 'slug-name'}

lookup = dict((v,k) for k, v in regex.groupindex.iteritems())
frags = [chr(i[1]) if i[0] == 'literal' else str(params[lookup[i[1][0]]]) \
    for i in parse(pattern) if i[0] != 'at']
print ''.join(frags)
>> page/slug-name/5/

This works by grabbing the raw opcodes via parse(), dumping the positional opcodes (they have 'at' for a first param), replacing the named groups, and concatenating the frags when it's done.


Django's django.core.urlresolvers.reverse_helper seems to be able to do this (funnily enough, it uses regex to parse regex).

You may be able to reuse reverse_helper and MatchChecker that it provides.

0

精彩评论

暂无评论...
验证码 换一张
取 消