ColdFusion in Context: New String Functions

Suppose you want new functions to work with strings. Sure, you could include procedural code wherever you want a modified or analyzed string, but functions would let you produce a half-dozen outputs in a single line of code. Here are some string functions to get you comfortable with the idea.

Put all this code in stringer.cfm.

Build a Form

Because they won't exist on the first pass, set defaults for a string input and for a checkbox input. The checkbox input will be used at the end of this demonstration to show a side effect of using multiple functions in the same page request. Build a standard form to be submitted to itself. When checked, the checkbox value will be "on". Convert this to the word "checked" - there are more elegant ways to do the conversion - so the checkbox will appear as it should.

<cfparam name="form.MyString" default="">
<cfparam name="form.Demo" default="">

<form action="stringer.cfm" method="post">
Enter a string; include spaces before,
inside, and after.<br>
Check the box if you want a surprise.<br>
<input type="string" name="MyString" 
value=<cfoutput>"#form.MyString#"</cfoutput>>
<cfif Demo is "on">
  <cfset Show="checked">
<cfelse>
  <cfset Show="">
</cfif>
<input type="checkbox" name="Demo"
<cfoutput>#Show#</cfoutput>>
<input type="submit" name="Dummy" value="OK">
</form>

Count Spaces

This isn't a terribly useful function, but the idea might lead you to something useful. It's here mostly as a poster child for watching your variable scopes. Open with cfscript, handle the situation where the string is empty, then do the real work of looping over the string to count spaces.

<cfscript>
function countSpace(InString) {
  if (len(trim(InString)) lt 1) {
    return 0;
  }
  Nr=0;
  StrLen=len(InString);
  // Can you find a leak?
  for (i=1; i le StrLen; i=i+1) {
    Spot=mid(InString,i,1);
    if (Spot is ' ') {
      Nr=Nr+1;
    }
  }
  return Nr;
}

Clip and Trim

This function is fairly useful. It trims a string, chops it to no longer than the desired length. Again dealing with empty input first, it returns the desired number of characters from a trimmed version of the string. Because strings are passed into the function by reference, no inputs are harmed in this experiment. However, notice that spaces bracketed by non-space characters may wind up at the end of the chopped string if the length is just right. If you adopt this tool for your own use, you should probably trim the result again prior to output where the available length exceeds the desired length (in the return at the very end of the function).

function clipTrim(InString, DesiredLength) {
  if (len(trim(InString)) lt 1) {
    return "";
  }
  if (len(trim(InString)) le DesiredLength) {
    return trim(InString);
  }
  return left(trim(InString), DesiredLength);
}
I find this function less useful. It clips the string but doesn't trim it first; so, the results are not what you might expect. Again, deal with empty input first, then with normal input.

function clip(InString, DesiredLength) {
  if (len(InString) lt 1) {
    return "";
  }
  if (len(InString) le DesiredLength) {
    return InString;
  }
  return left(InString, DesiredLength);
}

Something Unexpected

This last function is a switchable tattletale. If the Mode box is checked, its value will be "on". When its value is on, this function displays the value of the loop counter used by the countSpace function. Close the cfscript tag.

function sideEffect(Mode) {
  if (Mode is "on") {
    return "countSpace loop value leaked!  It's "&i;
  }
  else {
    return '';
  }
}
</cfscript>

Output

Show the result of using these functions. Display countSpace for the string. When the desired length is 5 characters, show clipTrim and clip for the string, surrounding their output with square brackets for clarity). Repeat for length 20. Then display the side effect.

<cfoutput>countSpace sees 
#countSpace(form.MyString)# spaces</cfoutput>
<p>
With a desired length of 5...<br>
<cfoutput>
<pre>
clipTrim says: [#clipTrim(form.MyString,5)#]
clip says: [#clip(form.MyString,5)#]
</pre>
</cfoutput>
<p>
With a desired length of 20...<br>
<cfoutput>
<pre>
clipTrim says: [#clipTrim(form.MyString,20)#]
clip says: [#clip(form.MyString,20)#]
<p>
#sideEffect(form.Demo)#
</cfoutput>
</pre>

Action and Discussion

Browse stringer.cfm. Notice that the string counter works and that clip and clipTrim do perform as advertised. Then check the box, MAKING SURE THE INPUT IS NOT EMPTY. The space counter loops over each character in the string, and its loop counter goes one beyond the length of the string. The value of the loop counter will be displayed by the apparently unrelated function named sideEffect.

Somehow, the loop counter has become available to all functions on the page.

The variables declared in a user-defined function are available to other user-defined functions unless you say something like "var i=0;" before the other statements in the function. In a reversal from some languages, saying "var" makes the variable local to a single function. Saying nothing, even for a loop variable, makes it global to all the user-defined functions in the same page request.

This result was unexpected, but if you're careful, user-defined functions can simplify your life. =Marty=