Return of the Friday Puzzler: Build a Regex Tester
A few days ago I talked about how I normally did quick regex tests: I would write up some code in a CFM page and run it in my browser. Obviously a dedicated tool is better and more efficient. But pretend for a moment that you didn't have such a tool. Or perhaps you are not at your own machine and just want to quickly test something.
Today's challenge is really trivial. Simply build a form that lets you input a string and a regex. Than show the results of ColdFusion using that regex on the string. In theory that should take you about two through three minutes, but consider adding some polish. For example - maybe Spry could be used to generate the results? I can think of a few other ideas as well to make the results pretty.
Enjoy. For those of you new to my little puzzlers (sorry it has been so long), do not remember the rules: Don't take more than five or ten minutes to finish the code. There is no prize - just the fun of coding. That's it!
Comments
This example does require xpath.js and SpryData.js.
If the code pasted below doesn't come out right, you can download the files here:
http://ken.auenson.com/examples/RegExTester.zip
doReplace.cfm
-----------------------------------
begin
-----------------------------------
<cfsetting showdebugoutput=false>
<cfparam name="URL.strSource" default="">
<cfparam name="URL.strRegExp" default="">
<cfparam name="URL.strSubStr" default="">
<cfparam name="URL.strScope" default="ONE">
<cfparam name="URL.IgnoreCase" default="True">
<cfif Len(URL.strSource) AND Len(URL.strRegExp) AND Len(URL.strSubStr) >
<cfif URL.IgnoreCase >
<cfoutput>#ReReplaceNoCase(URL.strSource, URL.strRegExp, URL.strSubStr, URL.strScope)#</cfoutput>
<cfelse>
<cfoutput>#ReReplace(URL.strSource, URL.strRegExp, URL.strSubStr, URL.strScope)#</cfoutput>
</cfif>
</cfif>
-----------------------------------
end
-----------------------------------
RegExTester.cfm
-----------------------------------
begin
-----------------------------------
<html>
<head>
<title>Friday Puzzler: RegEx Tester</title>
<script src="xpath.js" ></script>
<script src="SpryData.js" ></script>
<script>
Spry.Data.Region.debug = true;
function processRegEx() {
var srcValue = document.getElementById("strSource").value;
var regExValue = document.getElementById("strRegEx" ).value;
var substrValue = document.getElementById("strSubStr").value;
var scopeValue = document.getElementById("strScope" ).value;
var caseValue = document.getElementById("strCase" ).value;
var myURL = "doReplace.cfm?strSource=" + encodeURIComponent(srcValue )
+ "&strRegExp=" + encodeURIComponent(regExValue )
+ "&strSubStr=" + encodeURIComponent(substrValue )
+ "&strScope=" + encodeURIComponent(scopeValue )
+ "&IgnoreCase=" + encodeURIComponent(caseValue );
Spry.Utils.loadURL("GET", myURL,
false,
displayResult);
}
function displayResult(request) {
var resultblock = document.getElementById("resultblock");
resultblock.innerHTML = request.xhRequest.responseText;
}
</script>
<style>
.RegExResult {
color: #000000;
background-color: #FFECBC;
font-weight: bold;
}
</style>
</head>
<body>
<form id="RegExTestForm" action="null.html" method="post">
<h2>RegEx Tester</h2>
<table border="0">
<tr valign="top">
<td align="right">Source</td>
<td >
<input type="text" id="strSource" name="strSource" size="100" onKeyUp="processRegEx()">
</td>
</tr>
<tr valign="top">
<td align="right">RegEx</td>
<td >
<input type="text" id="strRegEx" name="strRegEx" size="100" onKeyUp="processRegEx()">
</td>
</tr>
<tr valign="top">
<td align="right">Replace With</td>
<td >
<input type="text" id="strSubStr" name="strSubStr" size="100" onKeyUp="processRegEx()">
</td>
</tr>
<tr valign="top">
<td align="right">Scope</td>
<td >
<select id="strScope" name="strScope" onChange="processRegEx()">
<option value="ONE" >One</option>
<option value="ALL" >All</option>
</select>
</td>
</tr>
<tr valign="top">
<td align="right">Ignore Case?</td>
<td >
<select id="strCase" name="strCase" onChange="processRegEx()">
<option value="True" >Yes</option>
<option value="False" >No </option>
</select>
</td>
</tr>
<tr>
<td>Result</td>
<td class="RegExResult" id="resultblock" ></td>
</tr>
</table>
</form>
</body>
</html>
-----------------------------------
end
-----------------------------------
So, what do you think?
just playing end user devils advocate because I'm bored :P
I have updated my code in my zip file to handle this.
It isn't pretty, but it works!
I plan to document it... you know... eventually!
http://ken.auenson.com/examples/RegExTester.zip
I'll paste all the code below, but here is a link to a zip of the files: http://www.filefactory.com/file/17fb35/
===========
index.cfm
===========
<!---
Regular Expression Tester
21 Nov 06
--->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitiona...;
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Regular Expression Tester</title>
<link rel="stylesheet" href="regExp.css" title="default style sheet for the regular expression tester" />
<script src="includes/SpryData.js" ></script>
<script type="text/javascript">
<!--- function to disable/enable the text field for replacement text and first/all--->
function changeFormFocus() {
var typeValue = document.getElementById("regExpType").value;
if(typeValue == 'validate') {
var scopeVal = document.getElementById("regExpScope");
scopeVal.value = "one";
document.forms[0].replTextField.disabled = true;
document.forms[0].regExpScope.disabled = true;
}
else {
document.forms[0].replTextField.disabled = false;
document.forms[0].regExpScope.disabled = false;
}
}
<!--- functions to do the callback and simple ajax to process form --->
function sndReq() {
var tmpExpression = document.getElementById("expression").value;
var tmpTextSample = document.getElementById("textSample").value;
var tmpRegExpType = document.getElementById("regExpType").value;
var tmpRegExpScope = document.getElementById("regExpScope").value;
var tmpReplTextField = document.getElementById("replTextField").value;
var tmpcaseSensitive = document.getElementById("caseSensitive").checked;
var myURL = "regExp.cfm?expression=" + encodeURIComponent(tmpExpression)
+ "&textSample=" + encodeURIComponent(tmpTextSample)
+ "®ExpType=" + encodeURIComponent(tmpRegExpType)
+ "®ExpScope=" + encodeURIComponent(tmpRegExpScope)
+ "&replTextField=" + encodeURIComponent(tmpReplTextField);
<!--- concatenante the checkbox --->
if(tmpcaseSensitive) {
myURL = myURL + "&caseSensitive=1";
}
Spry.Utils.loadURL("GET", myURL, false, handleResponse);
}
function handleResponse(request) {
if(request.xhRequest.readyState < 4){
document.getElementById("regExpResult").innerHTML = "Loading...";
}
if(request.xhRequest.readyState == 4){
var response = request.xhRequest.responseText;
document.getElementById("regExpResult").innerHTML = response;
}
}
</script>
</head>
<body onload="changeFormFocus()">
<!--- param expected form vars --->
<cfparam name="url.expression" default="" />
<cfparam name="url.textSample" default="Sample Text" />
<cfparam name="url.replTextField" default="Replacement Text" />
<cfparam name="url.regExpType" default="replace" />
<cfparam name="url.regExpScope" default="all" />
<div id="container">
<div class="formBlock">
<fieldset>
<legend>Regular Expression Input</legend>
<form method="post" name="regExpTester" action="index.cfm">
<div class="formRow">
<label class="formLabel">Regular Expression:</label>
<input class="formItem" type="text" id="expression" name="expression" size="50" tabindex="1" value="<cfoutput>#url.expression#</cfoutput>" />
</div>
<div class="formRow">
<label class="formLabel">Sample Text:</label>
<input class="formItem" type="text" id="textSample" name="textSample" size="50" tabindex="2" value="<cfoutput>#url.textSample#</cfoutput>" />
</div>
<div class="formRow">
<label class="formLabel">Type:</label>
<select id="regExpType" name="regExpType" onchange="changeFormFocus();" tabindex="3">
<option value="validate" <cfif url.regExpType EQ "validate">selected</cfif> >Validate</option>
<option value="replace" <cfif url.regExpType EQ "replace">selected</cfif> >Replace</option>
</select>
</div>
<div class="formRow">
<label class="formLabel">Scope:</label>
<select id="regExpScope" name="regExpScope" tabindex="4">
<option value="one" <cfif url.regExpScope EQ "one">selected</cfif> >First</option>
<option value="all" <cfif url.regExpScope EQ "all">selected</cfif> >All</option>
</select>
</div>
<div class="formRow">
<label class="formLabel">Replacement Text:</label>
<input class="formItem" id="replTextField" type="text" id="replTextField" name="replTextField" size="50" tabindex="5" value="<cfoutput>#url.replTextField#</cfoutput>" />
</div>
<div class="formRow">
<label class="formLabel"> </label>
<input type="checkbox" id="caseSensitive" name="caseSensitive" tabindex="6" <cfif StructKeyExists(url, "caseSensitive")>checked</cfif> /> <label for="caseSensitive">Case Sensitive</label>
</div>
<div class="formButtons">
<input type="button" onclick="sndReq()" name="Submit" value="Test Expression" />
</div>
</form>
</fieldset>
<fieldset>
<legend>Result</legend>
<div id="regExpResult"> </div>
</fieldset>
</div>
</div>
</body>
</html>
============
regExp.cfm
============
<!---
Regular Expression Tester
21 Nov 06
--->
<cfsetting showdebugoutput="no" />
<!--- param expected form vars --->
<cfparam name="url.expression" default="" />
<cfparam name="url.textSample" default="Sample Text" />
<cfparam name="url.replTextField" default="Replacement Text" />
<cfparam name="url.regExpType" default="replace" />
<cfparam name="url.regExpScope" default="all" />
<cfif StructKeyExists(url, "expression") AND StructKeyExists(url, "textSample")>
<cfset temp = "" />
<cftry>
<cfswitch expression="#url.regExpType#">
<cfcase value="replace">
<cfif StructKeyExists(url, "caseSensitive")>
<cfset temp = ReReplace(url.textSample, url.expression, url.replTextField, url.regExpScope) />
<cfelse>
<cfset temp = ReReplaceNoCase(url.textSample, url.expression, url.replTextField, url.regExpScope) />
</cfif>
<cfoutput>#temp#</cfoutput>
</cfcase>
<cfdefaultcase>
<!--- default to a validate/find --->
<cfif StructKeyExists(url, "caseSensitive")>
<cfset temp = ReFind(url.expression, url.textSample, 1, true) />
<cfelse>
<cfset temp = ReFindNoCase(url.expression, url.textSample, 1, true) />
</cfif>
<!--- if a position was found show it otherwise a not found msg --->
<cfif StructKeyExists(temp, "pos") AND temp.pos[1] gt 0>
<cfset intStartingPoint = temp.pos[1] />
<cfset intFoundLength = temp.len[1] />
<cfset intPreStrLen = intStartingPoint - 1 />
<cfset intPostStrLen = Len(Trim(url.textSample)) - ((intStartingPoint + intFoundLength) - 1) />
<!--- --->
<!---build up a highlighter --->
<!--- --->
<!--- make sure lefti snt a 0 --->
<cfif intPreStrLen>
<cfset strPreString = Left(url.textSample, intPreStrLen) />
<cfelse>
<cfset strPreString = "" />
</cfif>
<!--- make sure right isnt a 0 --->
<cfif intPostStrLen>
<cfset strPostString = Right(url.textSample, intPostStrLen) />
<cfelse>
<cfset strPostString = "" />
</cfif>
<cfset strFoundString = Mid(url.textSample, intStartingPoint, intFoundLength) />
<cfoutput>#strPreString#<span class="highlight">#strFoundString#</span>#strPostString# <Br /></cfoutput>
<cfelse>
<cfoutput>#url.expression# was <strong>not</strong> found in #url.textSample#</cfoutput>
</cfif>
</cfdefaultcase>
</cfswitch>
<cfcatch>
Unable to process that regular expression. Please check the validity of your form fields.
</cfcatch>
</cftry>
</cfif>
============
regExp.css
============
/*
Regular Expression Tester
21 Nov 06
*/
#container {
width:60%;
background-color:#DFE1F4;
padding: 8px;
border: 1px #737CCE solid;
}
#container .formBlock {
background-color:#fff;
border: 1px solid #C0C0C0;
padding: 4px;
}
#container legend{
font-family: Verdana, Arial, sans-serif;
font-weight: bold;
font-size:x-small;
color: #9195D2;
}
* html #container legend{
margin-bottom: 10px;
}
#container .formLabel {
padding-right: .2em;
width: 12em;
font-family: verdana, arial, sans-serif;
font-size:x-small;
margin-bottom: 8px;
float: left;
}
#container .formRow input {
background:#F5F1F2;
border: solid 1px #8484A1;
margin-bottom: 5px;
}
#container .formButtons {
text-align:center;
}
#container .formButtons input {
background-color:#DFE1F4;
border: solid 1px #737CCE;
font-family: verdana, arial, sans-serif;
font-size:x-small;
margin: 5px 0 0 0;
}
#container .radioLabel {
font-family: verdana, arial, sans-serif;
font-size:x-small;
}
#container .formRow #caseSensitive {
border:0;
background-color:#fff;
}
#container .formRow {
margin: 3px 0;
}
.highlight {
background-color:#FFFFB3;
font-weight:bold;
}
Will let you all download the code when I put it up later today... err... test it.. let me know what you think!
I wrote it using DreamWeaver...not.

<cfparam name="form.strPattern" default="">
<cfparam name="form.strReplace" default="">
<cfparam name="form.chkAll" default="false">
<cfform
action="#cgi.script_name#"
method="post"
format="flash"
skin="haloSilver"
width="300"
height="150"
timeout="100">
<cfinput type="text" name="strSource" label="Source String" value="#form.strSource#" validate="noblanks" message="The source string is required.">
<cfinput type="text" name="strPattern" label="Regex Pattern" value="#form.strPattern#" validate="noblanks" message="The regular expression pattern is required.">
<cfinput type="text" name="strReplace" label="Replace String" value="#form.strReplace#" validate="noblanks" message="The replacing string is required.">
<cfinput type="checkbox" name="chkAll" label="Replace All (optional)" checked="#form.chkAll#">
<cfinput type="submit" name="submit" value="Replace String">
</cfform>
<cfif cgi.request_method eq "post">
<cfoutput>
New String:
<cfif form.chkAll eq "yes">
#ReReplace(form.strSource,form.strPattern,form.strReplace,"ALL")#
<cfelse>
#ReReplace(form.strSource,form.strPattern,form.strReplace)#
</cfif>
</cfoutput>
</cfif>
Cheers!
Teddy