Using a function for regular expression substitution

naugiedoggie michael.a.powe at
Mon Aug 30 14:52:02 CEST 2010

On Aug 29, 1:14 pm, MRAB <pyt... at> wrote:
> On 29/08/2010 15:22, naugiedoggie wrote:

> > I'm having a problem with using a function as the replacement in
> > re.sub().

> > Here is the function:

> > def normalize(s) :
> >      return
> > urllib.quote(string.capwords(urllib.unquote('provider'))))
> This normalises the provider and returns only that, and none of the
> remainder of the string.
> I think you might want this:
> def normalize(s):
>      return s[ : s.start('provider')] +
> urllib.quote(string.capwords(urllib.unquote('provider')))) +
> s[s.start('provider') : ]
> It returns the part before the provider, followed by the normalised
> provider, and then the part after the provider.


Thanks for the reply.

There must be something basic about the re.sub() function that I'm
missing.  The documentation shows this example:

>>> def dashrepl(matchobj):
...     if == '-': return ' '
...     else: return '-'
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'

According to the doc, the modifying function takes one parameter, the
MatchObject.  The re.sub function takes only a compiled regex object
or a pattern, generates a MatchObject from that object/pattern and
passes the MatchObject to the given function. Notice that in the
examples, the re.sub() returns the entire line, with the changes made.
But the function itself returns only the change.  What is happening
for me is that, if I have a line that contains
&Search_Provider=chen&p=value, the processed line ends up with

Now, I did follow up with your suggestion.  `s' is actually a
MatchObject (bad param naming on my part, I started out passing a
string into the function and then changed it to a MatchObject, but
didn't change the param name), so I made the following change:

return line[s.pos : s.start('provider')] + \
urllib.quote(string.capwords(urllib.unquote('provider')))) + \
        line[s.end('provider') : ]

In order to make this work (finally), I had to make the processing
function look like this:

def processLine(l) :
        global line
        line = l
        provider = getProvider(line)
        if provider == "No Provider" : return line
        scenario = getScenario(line)
        if filter (lambda a: a != None, [getOrg(s,scenario) for s in
orgs]) == [] :
            line = re.sub(provider_pattern,normalize,line)
        else :
            line.replace(provider_parameter, org_parameter)
        return line

And then the call:

lines = fileReader.readlines()
[ fileWriter.write(l) for l in [processLine(l) for l in lines]]

Without this complicated gobbledigook, I could not get the correct
result.  I hate global vars and I completely do not understand why I
have to go through this twisting and turning to get the desired

[ ... ]

> These can be replaced by:
>         if 'Search_Type' in line and 'Search_Provider' in line:
> >            re.sub(provider_matcher,normalize,line)
> re.sub is returning the result, which you're throwing away!
>                 line = re.sub(provider_matcher,normalize,line)

I can't count the number of times I have forgotten the meaning of
'returns a string' when reading docs about doing substitutions. In
this case, I had put the `line = ' in and taken it out.  And I should
know better, from years of programming in Java, where strings are
immutable and you _always_ get a new, returned string.  Should be
second nature.

Thanks for the help, much appreciated.


More information about the Python-list mailing list