ColdFusion REFind() Cannot Find The Empty String

Posted December 16, 2008 at 8:56 AM by Ben Nadel

Tags: ColdFusion

When you step back and think about it, this makes perfect sense; but, yesterday, for a few minutes, I was completely stumped as to why my ColdFusion REFind() method call was not working. I was using regular expressions to validate strings and for strings that could be zero or more characters, I was using a regular expression that looked like this:

^.{0,50}$

It worked for strings that had positive lengths, but failed for zero length strings:

  • <!--- Create the value to match with the regular expression. --->
  • <cfset strValue = "" />
  •  
  • <!---
  • Create a regular expression that matches ZERO or more
  • characters in a complete string.
  • --->
  • <cfset strRegEx = "^.*$" />
  •  
  • <!---
  • Check to see if the regular expression pattern was
  • found in our empty string.
  • --->
  • <cfif REFind( strRegEx, strValue )>
  •  
  • Found!
  •  
  • <cfelse>
  •  
  • Not Found!
  •  
  • </cfif>

Running the above code gives us the following output:

Not Found!

If you run this in any kind of third-party regular expression tester, like The RegEx Coach, it will work fine. The problem is not in the regular expression. In fact, there really is no problem at all. The reason this doesn't work is that it cannot co-exist with the one-based indexing used in ColdFusion. The REFind() and REFindNoCase() functions return "the position in the string where the match begins;" since ColdFusion strings begin at index one, it is impossible to return the a match that has no length as it will also have no index. In other words, for the REFind() method to match the empty string it would have to also confirm that the target string has a length, which would be a contradiction.

In order to get this to work, I had to switch over to using the Java Pattern object's Matches() method:

  • <!--- Create the value to match with the regular expression. --->
  • <cfset strValue = "" />
  •  
  • <!---
  • Create a regular expression that matches ZERO or more
  • characters in a complete string.
  • --->
  • <cfset strRegEx = "^.*$" />
  •  
  • <!---
  • Check to see if the regular expression pattern was found in
  • our empty string. To do this, we are going to use the Java
  • Pattern object. Not only is Java is used to using zero-based
  • indexing, but this method returns a BOOLEAN rather than a
  • match offset.
  • --->
  • <cfif CreateObject( "java", "java.util.regex.Pattern" ).Matches(
  • JavaCast( "string", strRegEx ),
  • JavaCast( "string", strValue )
  • )>
  •  
  • Found!
  •  
  • <cfelse>
  •  
  • Not Found!
  •  
  • </cfif>

This time, when we run the code above we get the following output:

Found!

As you can see, the Java Pattern object was able to match the empty string.

Now, just to make sure that I was really on the right track, I tested to see if the REReplace() method, which doesn't care about string indexes, would match the empty string:

  • <!--- Create the value to match with the regular expression. --->
  • <cfset strValue = "" />
  •  
  • <!---
  • Create a regular expression that matches ZERO or more
  • characters in a complete string.
  • --->
  • <cfset strRegEx = "^.*$" />
  •  
  • <!---
  • Replace the regular expression pattern match with
  • a new value.
  • --->
  • <cfset strValue = REReplace(
  • strValue,
  • strRegEx,
  • "Replaced!",
  • "one"
  • ) />
  •  
  • <!--- Output our new string. --->
  • #strValue#

When we run this code, we get the following output:

Replaced!

That works just fine. So, like I said, when you step back and think about what the ColdFusion REFind() and REFindNoCase() methods actually do, it becomes obvious that they cannot match the empty string. But, when you have your nose deep in code, sometimes it's not that obvious.

And, as one more test, I just wanted to see if ColdFusion 8's new REMatch() function could match the empty string:

  • <!--- Create the value to match with the regular expression. --->
  • <cfset strValue = "" />
  •  
  • <!---
  • Create a regular expression that matches ZERO or more
  • characters in a complete string.
  • --->
  • <cfset strRegEx = "^.*$" />
  •  
  • <!--- Get all the matches for our empty string. --->
  • <cfset arrMatches = REMatch( strRegEx, strValue ) />
  •  
  • <!--- Output the first value. --->
  • Index 1: [#arrMatches[ 1 ]#]

When we run this, we get the following output:

Index 1: []

As you can see, ColdFusion's REMatch() does successfully match and return the empty string.



Reader Comments

Dec 16, 2008 at 2:33 PM // reply »
39 Comments

Ben,

You're too good looking to be talking about such nerdy topics. You should be wearing glasses with tape on them, a button down shirt with a pocket protector, etc. :)

But seriously, this is just more fuel for the "CF should have 0-based indexing" fire. It has always bothered me...


Dec 16, 2008 at 3:07 PM // reply »
11,241 Comments

@Jake,

Ha ha :) Thanks. Yeah, 0 vs. 1 is an interesting topic. I do like that having many things 1-based makes some boolean logic much more easy to write. For example:

<cfif ListFind()>

... is (to me) much more readable and intent-meaningful than:

<cfif (ListFind() GTE 0)>

But, that might be a small trade-off when considering a whole bunch of other things including cross-language compatibility.


Dec 16, 2008 at 6:14 PM // reply »
39 Comments

Yeah, good point, boolean logic is easier with 1-based indexing.


Dec 17, 2008 at 9:50 AM // reply »
7 Comments

This is just one of the many reasons why I prefer Pattern matching to the built in Regex functions. The expressions that Pattern recognizes are broader and more accurate then the built in functions.


Dec 17, 2008 at 9:54 AM // reply »
11,241 Comments

@Jon,

The Pattern matching / matcher is also much faster and provides access to individual captured groups. It's truly a wonderful library to have at our disposal.


Dec 17, 2008 at 3:09 PM // reply »
7 Comments

@Ben

Yep, that's why I worked through a list of Pattern based utility functions over on my site :)


Dec 18, 2012 at 3:35 PM // reply »
2 Comments

Please any one me.. how can i compare 2 blog posts using coldfusion function or any method. i need to remove blog if two posts are 80% matching


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 22, 2013 at 12:44 PM
Ask Ben: Query Loop Inside CFScript Tags
In cf10, if you call a function that has: local.result = {}; local.result.msg = ""; local.svc = new query(); local.svc.setSQL("SELECT * FROM..."); local.obj = local.svc.exe ... read »
May 22, 2013 at 12:29 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben: What version of Java are you using? Also, did you test users.id to see what Java reports as the data type? I wonder if it's not a Java primitive data type, but getting returned as something ... read »
May 22, 2013 at 11:47 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dana, Awesome - so it looks like this bug was fixed in ColdFusion 10. Thanks so much for double-checking that. ... read »
May 22, 2013 at 11:37 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
When I c&p and run on cf10, I get: Selected User IDs: 1,4 User 1 selected: YES - YES User 2 selected: NO - NO User 3 selected: NO - NO User 4 selected: YES - YES User 5 selected: NO - ... read »
May 22, 2013 at 11:27 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Tom, Good thought, but no dice. Both of these still exhibit the same behavior: users.id[ users.currentRow ] users[ "id" ][ users.currentRow ] It's just something whacky happening with ... read »
May 22, 2013 at 11:07 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
Could your problem be that "users.id" is actually an ARRAY, not a single value? Perhaps try it again with "users.id[1]" (I only have CF8 here at work). ... read »
May 22, 2013 at 7:52 AM
Nested Views, Routing, And Deep Linking With AngularJS
Hi, Just a quick thank you. As it happens, for my own purposes, the pending ui-router work being done in native angular is likely the one I'll adopt, but your exploration, code and documentation of ... read »
May 22, 2013 at 4:43 AM
How Do You Use The ColdFusion CFParam Tag?
'<cfparam>' or 'isDefined()and <cfset>' performs the same task.Is there any difference? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools