I had blogged a little while ago about how awesome it was in Javascript that the String::replace() method could take a function pointer as its replace-with argument and have each match get thrown to the function for evaluation. Unfortunately, I cannot find a native way to do this in ColdFusion. It seems that the replace-with argument gets evaluated once - there is no per-match evaluation.
This, however IS possible if you leverage the Pattern Matcher in Java. That Pattern Matcher runs through a string and finds every matching substring. So, what I was trying to do was mimic the way Javascript performs this. Like I have demonstrated before, Javascript can take either a string as the replace-with argument OR a function pointer. I wanted to do the same thing:
Launch code in new window » Download code as text file »
Notice there that the REReplace method starts with "J". That is because it is performing JAVA replace calls. You have to be careful with Java as it uses a slightly different notation. Groups are reference via the dollar sign "$" and NOT the "\" like ColdFusion regular expression replace does.
Ok, so on to the meat of the demonstration. Let's take a look at the user defined function I wrote. I have broken up the lines a bit to decrease horizontal scrolling (not how it is in the code):
Launch code in new window » Download code as text file »
The first thing you should see here is that the argument "Target" is of type "ANY". This is because it has to be able to accept both a string value AND a function pointer. The second thing you should notice here is that the function branches based on the type of Target argument. If it is a simple value (ie. a string), it merely returns the results of standard Java replace() method calls. It only goes through the extra processing of looping through the matches if it HAS to. The third thing you should notice is that when the function pointer gets evaluated (called as a function), it gets passed two arguments: the string that was matched and an array of the groups that matched.
Let's just look at that last thing for a second. Let's say you had the following expression:
Launch code in new window » Download code as text file »
... and it matched the following text:
Launch code in new window » Download code as text file »
... The first argument passed to your target function would be the entire string. The second argument would be the array of matched groups which would be:
You have to design your helper function to handle this functionality. Of course, you have the option to ignore the second argument all together and just use the matched string.
Let's look at what the helper function might be:
Launch code in new window » Download code as text file »
Now, let's pull it all together. Let's set up some demo text to play around with:
Launch code in new window » Download code as text file »
Let's try calling this in the two optional ways, starting with your standard string replace features:
Launch code in new window » Download code as text file »
In this case, we are just bolding the matched string and it gives us:
I saw this girl over at the market the other
day shopping for melons. I was shopping for
melons, not her... That is to say, I was
shopping for melons, not shopping for her.
Anyway, the was very cute. So cute in fact,
that I could not work up the nerve
to talk to her. Oh well.
Now, let's get crazy sexy and call it using our helper function pointer:
Launch code in new window » Download code as text file »
This passes each match to our helper function and gives us:
I saw this lady over at the market the other
day shopping for juicy melons. I was shopping for
juicy melons, not her... That is to say, I was
shopping for juicy melons, not shopping for her.
Anyway, the was very wicked cute. So wicked cute in fact,
that I could not work up the nerve to
talk to her. Oh well.
How awesome is that? This might take you a minute to fully grasp how powerful this might be. Have you ever tried to create insanely complex regular expressions? Imagine being able to make much more simple regular expressions and let the Helper function do most of the heavy lifting. You have access to the ENTIRE COLDFUSION TAG/FUNCTION LIBRARY. That is power my friends - much more power than regular expressions along will give you.
It's not quite as powerful and flexible as Javascript implementation, but hey, it's pretty darn nifty.
Download Code Snippet ZIP File
Comments (4) | Post Comment | Ask Ben | Permalink | Other Searches | Print Page
SQL Date/Time BETWEEN And Comparison Operators Work With Floats
ColdFusion JavaCast() Adds No Performance Hit
This is a test comment. I have updated my de-spamming technique and just needed to test it.
Posted by Ben Nadel on Aug 4, 2006 at 9:04 AM
Hi Ben. I'm using(like you) local(cfset local = StructNew()) structure to initialize variables inside functions. Better practices... But I inserted several variables including(instead you) function parameters. What do you think about? Cheers
Posted by Marco Antonio C Santos on Jan 16, 2008 at 12:44 PM
@Marco,
You can put anything you want into LOCAL. I don't see the need to insert the function parameters as they already exist in the ARGUMENTS scope. However, it should be fine to do so.
Posted by Ben Nadel on Jan 16, 2008 at 1:37 PM
Once again, I needed some CF code and rather than having to figure it out myself, you've already written it, and come up with a custom function to access it. Brilliant! :)
Thanks again.
Posted by Gareth on Mar 31, 2008 at 2:01 PM