Capturing the power of re.split
A couple of Python tips. The first is really a tip for Mozilla/Firebird: You can set up a Custom Keyword for instantly accessing Python module documentation using the string www.python.org/doc/current/lib/module-%s.html—I have this set up as pydoc, so I can type pydoc re to jump straight to the re module documentation. I only set it up half an hour ago and I’ve already used it about a dozen times.
The second tip is so powerful I’ve been kicking myself for not finding out about it sooner. It relates to the regular expression module’s re.split() function. Just like string.split(), this lets you split up a string based on a certain token. With string.split() you the token you split on isn’t included in the resulting array:
>>> 'pipe|separated|values'.split('|')
['pipe', 'separated', 'values']
This is also true of re.split:
>>> splitter = re.compile('<.>')
>>> splitter.split('hi<a>there<b>from<c>python')
['hi', 'there', 'from', 'python']
Here’s the magic part though. If you put part or all of the regular expression in parenthesis the separating tokens get included in the resulting list:
>>> splitter = re.compile('(<.>)')
>>> splitter.split('hi<a>there<b>from<c>python')
['hi', '<a>', 'there', '<b>', 'from', '<c>', 'python']
Why is this a big deal? Because it suddenly makes writing simple parsers and tokenisers a whole heck of a lot easier. Using the above example, say you wanted to do something with each of the <?> style tags. You can just iterate through the resulting list identifying each tag using the regular expression you’ve already compiled and then altering just those list items, before joining the whole list back together again at the end.
Simple parsing and replacement of easily identified tags can already be achieved using the re.sub() method, which allows you to provide a callback function to process each matching token. The difference with using re.split() is that you can easily take in to account the order of the tokens, allowing you to build systems that can use special tags to define areas of documents without getting confused by nesting tag sets. As a simple example, you could build a basic event based XML parser using just a couple of expressions. In fact, I discovered this technique while examining the source code for the tinpy tiny python template module, which gives a clue to why I’m so interested in it.
Having discovered this feature in Python, I just had to see if it existed in other languages as well. Unsurprisingly it does; PHP’s preg_split offers an optional PREG_SPLIT_DELIM_CAPTURE flag (added in PHP 4.0.5) and Javascript has similar behaviour to Python, including the splitting token if it is wrapped in parentheses.
I’m probably the last person to find out about this, but it’s such a useful technique I felt I just had to share it with the world.
Mark Eichin - 26th October 2003 07:21 - #
Chris - 26th October 2003 15:23 - #
fwiw, having implemented them both ;-), I tend to prefer re.findall("token|sep") over re.split("sep").
(but that's probably only because I got used to that style back in the pre-2.2 days, when findall was written in C but split was written in Python. in 2.2 and later, they're both about as fast as they can be.)
Fredrik Lundh - 27th October 2003 11:55 - #
Greg - 28th October 2003 05:19 - #
This feature was most likely dreamt up by Larry Wall & Co.
The above is from the
Changesfile of Perl 3.0, released on October 18th, 1989 (making it older than any Python source code).Arien - 28th October 2003 07:09 - #
Joseph Reagle - 31st October 2003 20:58 - #
Steve Ferg - 9th May 2004 01:43 - #
Holy cow, why didn't I ever think of 'pydoc %s'.. I already use a few of these for other, less obviously useful things.
*sound of palm meeting forehead*
Cory Dodt - 21st September 2004 21:52 - #
Nico - 1st January 2005 21:35 - #
Tom W.M. - 13th April 2006 08:17 - #
Paul C - 29th June 2006 04:23 - #