RESplit() - Splitting Strings With Regular Expressions In ColdFusion

Posted November 19, 2010 at 10:24 AM by Ben Nadel

Tags: ColdFusion

ColdFusion 8 added the ability for the listToArray() function to include empty fields (rather than just skipping over them). Then, ColdFusion 9 added the ability for the listToArray() function to use multi-character delimiters (rather than using each character as a separate delimiter). This is some great functionality; however, I have a love affair with Regular Expressions and I thought it would be nice to have a splitting function that used patterns rather than static delimiters.

As of ColdFusion 8, we have the reMatch() function. This allows us to extract values that match a given pattern. What I've coded here is reSplit(). This allows us to extract values that exist between matches of a given pattern. In essence, reSplit() lets you view a string as a list in which the value delimiters are regular expression pattern matches.

reSplit( regexPattern, string ) :: Array

The reSplit() functionality builds on top of Java's String::split() method. However, rather than using this as an "undocumented" feature, I am using JavaCast() to ensure that I'm dealing with a valid Java string before any splitting is applied.

  • <cffunction
  • name="reSplit"
  • access="public"
  • returntype="array"
  • output="false"
  • hint="I split the given string using the given Java regular expression.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="regex"
  • type="string"
  • required="true"
  • hint="I am the regular expression being used to split the string."
  • />
  •  
  • <cfargument
  • name="value"
  • type="string"
  • required="true"
  • hint="I am the string being split."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!---
  • Get the split functionality from the core Java script. I am
  • using JavaCast here as a way to alleviate the fact that I'm
  • using *undocumented* functionality... sort of.
  •  
  • The -1 argument tells the split() method to include trailing
  • parts that are empty.
  • --->
  • <cfset local.parts = javaCast( "string", arguments.value ).split(
  • javaCast( "string", arguments.regex ),
  • javaCast( "int", -1 )
  • ) />
  •  
  • <!---
  • We now have the individual parts; however, the split()
  • method does not return a ColdFusion array - it returns a
  • typed String[] array. We now have to convert that to a
  • standard ColdFusion array.
  • --->
  • <cfset local.result = [] />
  •  
  • <!--- Loop over the parts and append them to the results. --->
  • <cfloop
  • index="local.part"
  • array="#local.parts#">
  •  
  • <cfset arrayAppend( local.result, local.part ) />
  •  
  • </cfloop>
  •  
  • <!--- Return the result. --->
  • <cfreturn local.result />
  • </cffunction>
  •  
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  •  
  • <!--- Create a list of values in which some are empty. --->
  • <cfset womenList = ",Katie,,Jill,Sarah," />
  •  
  • <!--- Split this list, using the comma as our pattern. --->
  • <cfset women = reSplit( ",", womenList ) />
  •  
  • <!--- Output the resultant collection. --->
  • <cfdump
  • var="#women#"
  • label="reSplit() Women"
  • />

As you can see, our value argument is passing through JavaCast() before the .split() method is invoked. This split() method takes a regular expression and returns a typed String array. This typed string array then needs to be converted to a valid ColdFusion array.

When we run the above code, we get the following CFDump output:

 
 
 
 
 
 
RESplit() Uses Regular Expression Pattern Based String Splitting In ColdFusion. 
 
 
 

As you can see, our list was split on the regular expression pattern, ",". This not only split the list, but it maintained the empty values between adjacent delimiters.

NOTE: Because our pattern, in this case, was nothing more than a comma, the same outcome could have been handled with listToArray() and the optional third argument, "includeEmptyFields."

This kind of pattern-based splitting could be great for parsing simple CSV data (ie. data that doesn't have any embedded special characters). Because CSV row delimiters might use the new line, the carriage return, or a combination of the two depending on the originating operating system, we can't really use the listToArray() function. Even with ColdFusion 9's optional argument, "multiCharacterDelimiter," there'd be no way to handle both empty lines as well as the variations in row delimiter.

With pattern-based splitting, however, delimiter variations become much easier:

  • <!--- Define the tab for our field delimiter. --->
  • <cfset tab = chr( 9 ) />
  •  
  • <!--- Define our tab-delimited data. --->
  • <cfsavecontent variable="csvData">
  • <cfoutput>
  • Name#tab#Age#tab#Hair
  • Katie#tab#29#tab#Brown
  • <!--- Totally empty row. --->
  • Jill#tab##tab#Brown
  • #tab##tab#
  • Sarah#tab#33#tab#
  • </cfoutput>
  • </cfsavecontent>
  •  
  •  
  • <!---
  • Remove non-relevant white space - ie. remove the leading or
  • trailing line breaks, but do not remove any TABS.
  • --->
  • <cfset csvData = reReplace(
  • csvData,
  • "^[\r\n]+|[\r\n]+$",
  • "",
  • "all"
  • ) />
  •  
  •  
  • <!--- Get the rows using the line breaks. --->
  • <cfset rows = reSplit( "\r\n?|\n", csvData ) />
  •  
  • <!---
  • Now that we have the rows, loop over them and split each row
  • value by the tab-delimiter. This will result in an array of
  • arrays.
  • --->
  • <cfloop
  • index="rowIndex"
  • from="1"
  • to="#arrayLen( rows )#"
  • step="1">
  •  
  • <!---
  • Convert the row data to an array of field values (as
  • delimited by Tabs).
  • --->
  • <cfset rows[ rowIndex ] = reSplit( tab, rows[ rowIndex ] ) />
  •  
  • </cfloop>
  •  
  • <!--- Output our resultant CSV data. --->
  • <cfdump
  • var="#rows#"
  • label="Parsed CSV Data"
  • />

As you can see, we are splitting the CSV data based on the row-delimiter pattern:

\r\n?|\n

We are then splitting each resultant row on the field-delimiter pattern:

tab

NOTE: Our "tab" here is the variable containing the tab character literal.

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

 
 
 
 
 
 
RESplit() Uses Regular Expression Pattern Based String Splitting Which Can Be Used To Parse CSV Data. 
 
 
 

reMatch() added some awesome extraction functionality in ColdFusion 8; I think that reSplit() would be the prefect complement to that regular expression based parsing.




Reader Comments

Nov 19, 2010 at 10:36 AM // reply »
319 Comments

Any particular reason you would use Java over a completely native CFML one? Ala http://www.cflib.org/udf/resplit

(Holy crap - look at the date that was released!)


Nov 19, 2010 at 10:43 AM // reply »
11,246 Comments

@Ray,

Ha ha, "requires ColdFusion 5" :)

In my experience, I've just found that doing anything with regular expressions is faster when you dip down into the Java layer. Plus, the Java String object already has a split() method, so it just seemed like the easiest approach.


Nov 19, 2010 at 10:45 AM // reply »
319 Comments

Sensible enough to me.


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 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools