Here is something I've never tried to do before with regex - match multiple "rules" but within one regex. Consider for example password validation. Normally this requires a string pass multiple rules:

  • Must be N characters long
  • Must contain lower case characters
  • Must container upper case characters

I can do any of those rules easily enough but in the past I've done it "long" hand:

<cfset s = ["aaaa","aAa","AAAA","a9", "A9", "aA9","aaaAAA7"]>

<cfloop index="test" array="#s#"> <cfoutput>#test# ok? </cfoutput>

<cfif len(test) gte 7 and reFind("[a-z]", test) and reFind("[A-Z]", test)> yes <cfelse> no </cfif><br/>

</cfloop>

That works - but it seemed like there must be some way with regex to say "I want to ensure A matches, and B, and C, but I don't care where." My Google-fu failed until I came across this excellent blog post: Password Validation via Regular Expression. In this blog entry, Nilang Shah, makes use of a "positive lookahead." These are items you can ensure match in a regex but don't get returned in the match.

Let me be honest - I don't quite get how this stuff works. His example though worked perfectly. I took his third example and removed the requirement for a special character and got this:

<cfset s = ["aaaa","aAa","AAAA","a9", "A9", "aA9","aaaAAA7"]>

<cfloop index="test" array="#s#"> <cfoutput>#test# ok? </cfoutput> <cfset regex = "^.(?=.{7,})(?=.\d)(?=.[a-z])(?=.[A-Z]).*$">

<cfif reFind(regex, test)> yes <cfelse> no </cfif><br/>

</cfloop>

I don't quite get why we have to anchor it nor do I get the .* in the look aheads. But I can say it works great.