Last week, on a very old post about the importance of escaping the "$" and "\" characters in appendReplacement() calls on the Java Pattern Matcher, Rob asked me why I wasn't just using the quoteReplacement() method. To be honest, I had never seen this method before. After a quick Google search, I found that this method will escape your replacement text in such a way that none of its characters will hold any special meaning. Typically, if you have a "$" in your replacement text, the Matcher is expecting it to be followed by a numeric value in reference to a captured group. The "\", on the other hand, is expected to be escaping characters. To stop the Matcher from performing such evaluation, you can pass your replacement text through the intermediary method call, quoteReplacement():
<!--- Store some text to search / replace. ---> <cfset text = "Let's make some cash money." /> <!--- Create the pattern we are going to search form. ---> <cfset pattern = createObject( "java", "java.util.regex.Pattern" ) .compile( javaCast( "string", "money" ) ) /> <!--- Create our pattern matcher based on the target text. ---> <cfset matcher = pattern.matcher( javaCast( "string", text ) ) /> <!--- Create a string buffer to hold our replacement text. ---> <cfset buffer = createObject( "java", "java.lang.StringBuffer" ) .init() /> <!--- While we can find matches for "money", let's replace them with the dollar sign. ---> <cfloop condition="matcher.find()"> <!--- Replace with $. Notice that we are passing the replacement text through the quoteReplacement() method; this will escape special characters like the "$" and "\". ---> <cfset matcher.appendReplacement( buffer, matcher.quoteReplacement( javaCast( "string", "$" ) ) ) /> </cfloop> <!--- Add the rest of the target string to the buffer. ---> <cfset matcher.appendTail( buffer ) /> <!--- Output the full replacement. ---> Result: #buffer.toString()#
As you can see, we are using a round-about way (for demo purposes) to replace the pattern, "money," with the string, "$". As we perform the replacement, we pass our replacement string through the quoteReplacement() method; and, when we run the above code, we get the following output:
Result: Let's make some cash $.
If we did not use quoteReplacement(), the Java Matcher would have expected a captured group reference and would have thrown the following error:
String index out of range: 1 null
This is a pretty convenient method and it makes me frustrated that I had never seen it before, especially since I've used the Matcher object so many times. At first, I thought it was just laziness in my exploration of the Matcher API; but, this morning, when I went to find it again, I realized what happened. As I started to write this post, I did a Google search for "java 2 matcher" in order to look at the method definition. This search got me to the Matcher documentation for Java 1.4.2 - the one I usually use. Low and behold, there was no quoteReplacement() method!
At first, I thought I was going crazy! How was it here last week and not this week? Then it dawned on me: ColdFusion now uses the newer JVM with Java 6. For the past few years, I've been explicitly searching for the Java 2 docs to make sure that the docs were the first Google search results. That was fine years ago when I started, but now, I'm 2 Java versions behind on my research and development! Shame shame! It looks like a good deal has been added to the API in the last few versions.
A big thanks to Rob for not only pointing out a useful Java Matcher method - quoteReplacement() - but also for making me realize how out of date my Java research actually was. Looks like it's time to catch up on what's new in the Java language.
More blog fodder!
I hope so :)
Thanks again Rob :)
Just wanted to say thanks for posting this! I came across this today and it has been a great help :)
Cool - glad to help. Now, when I look things up on the Java docs, I always make it a point to Google "Java 6" when I do my searching.
How crazy is that. I had the exact same issue and the quoteReplace solved the issue.
I myself also have never seen or heard about this method - seems that it was not needed with java 1.4.x but only with recent versions ie. >=1.5.
But thanx for painting a clear picture!
My pleasure! It looks like when you Google for anything doc-related to Java, the 1.4.2 library seems to be the thing you get. Makes me wonder how much juiciness I've been missing out on!