Listing 1

<cfparam name="attributes.str_Filename" default="">
<cfparam name="attributes.str_LineDelimiter" 
default="#chr(13)##chr(10)#">
<cfparam name="attributes.str_TokenDelimiter" 
default=",">

<cffunction name="TokenizeLine" returntype="array">
<cfargument name="str_Content" type="string" 
required="true">
<cfargument name="str_Delimiter" type="string" 
required="true">

<cfset var ar_Tokens = arrayNew(1)>
<cfset var int_CurrPos = 1>
<cfset var int_NextPos = 1>
<cfset var str_Token = "">

<cfscript>
int_NextPos = 
REFind("#arguments.str_Delimiter#|$", 
arguments.str_Content, int_CurrPos);
while (int_NextPos gt 0)
{
str_Token = mid(arguments.str_Content, 
int_CurrPos, int_NextPos - int_CurrPos);
arrayAppend(ar_Tokens, str_Token);
int_CurrPos = int_NextPos + 
len(arguments.str_Delimiter);
int_NextPos = 
REFind("#arguments.str_Delimiter#|$", 
arguments.str_Content, int_CurrPos);
}
// If the line ends with a token, add
// an extra empty element to the array
if (len(arguments.str_Content) gt 0
and mid(arguments.str_Content, 
len(arguments.str_Content), 1) 
is arguments.str_Delimiter)
{
arrayAppend(ar_Tokens, "");
}
</cfscript>
<cfreturn ar_Tokens>
</cffunction>

<cfif ThisTag.ExecutionMode IS "Start">
<!--- Read the file and inititialize position 
and caller variables --->
<cffile action="read" 
file="#attributes.str_Filename#" 
variable="str_Content">
<cfset int_CurrPos = 1>
<cfset caller.TextParse.str_Line = "">
<cfset caller.TextParse.ar_Tokens = arrayNew(1)>
</cfif>

<!--- Find the next delimiter. The regular
expression "#attributes.str_LineDelimiter#|$"
finds the next line delimiter OR the end of 
the string. --->
<cfset int_NextPos = 
REFind("#attributes.str_LineDelimiter#|$", 
str_Content, int_CurrPos)>

<cfif int_NextPos gt 0>
<!--- Get and return the line and tokens --->
<cfset str_Line = mid(str_Content, 
int_CurrPos, int_NextPos - int_CurrPos)>
<cfset caller.TextParse.str_Line = str_Line>
<cfset caller.TextParse.ar_Tokens = 
TokenizeLine(str_Line, 
attributes.str_TokenDelimiter)>

<!--- reset the current position --->
<cfset int_CurrPos = int_NextPos + 
len(attributes.str_LineDelimiter)>
</cfif>

<cfif ThisTag.ExecutionMode IS "End">
<!--- Keep looping as long as we have 
content to process --->
<cfif int_NextPos gt 0>
<cfexit method="Loop">
</cfif>
</cfif>