I have a csv file composed from a header line (field list) and several detalis lines (details for each Customer).
This file contain a lot of unused fileds so I tried to rebuild a clean file in which I put only fields I need.
for that I loop over the header line, I get the index (position) of the field I need and stored it in a variable:
<cfset FirstName_Pos = listfind(header,'FirstName',';')>
<cfset LastName_Pos = listfind(header,'LastName',';')>
The separated header fields is the ';' character.
After that I retreive the positions of all needed fields, I create a new file to put in the desired info of each line
<cffile action="rename" source="#LocalPath#/#FileName#" destination="#LocalPath#/Source_#FileName#">
<cfset NewFile = FileOpen('#LocalPath#/#FileName#','Append')>
<cfset Newheader = 'FirstName,LastName'>
<cfset fileWriteLine(NewFile, Newheader)>
<cfloop file="#LocalPath#\Source_#FileName#" index="line">
<cfset count = count + 1>
<cfif count GTE 2>
<cfset FirstName= listgetat(line,FirstName_Pos,';',1)>
<cfset LastName= listgetat(line,LastName_Pos,';',1)>
<cfset detail = '#FirstName#,#LastName#'>
<cfset fileWriteLine(NewFile, detail)>
</cfif>
The problem is that in the details lines of the original file there are some fields written as follow :
"#08/04/14 23:00;08/05/14 23:00#"
i.e the field contains the ';' character which is my separated fields character I used in the listgetat function
Therefore, I get non desired value in the variable FirstName and LastName.
Considering that the original file contain the following info:
USERID;Post;FirstName;Date1;Mail;Date2;LastName;Telephone
123;Engineer;Alan;"#08/04/14 23:00;08/05/14 23:00#";alan#yahoo.fr;"#10/04/14 11:00;10/05/14 11:00#";Jones;0624262589
I get :
FirstName;LastName
Alan;"#10/04/14 11:00
instead of
FirstName;LastName
Alan;Jones
I get the idea to loop over all details line of the original file and replace the ';' charcater with a space or blank character using regular expression only on fields having the same format "#08/04/14 23:00;08/05/14 23:00#".
(The date change of course from one field to another and from one raw to another)
<cfloop file="#LocalPath#\Source_#FileName#" index="line">
<cfset newline = rereplace(line,'"##[^\w.];[^\w.]##"','"##[^\w.] [^\w.]##"','all')>
<cfset count = count + 1>
<cfif count GTE 2>
<cfset FirstName= listgetat(newline,FirstName_Pos,';',1)>
<cfset LastName= listgetat(newline,LastName_Pos,';',1)>
<cfset detail = '#FirstName#,#LastName#'>
<cfset fileWriteLine(NewFile, detail)>
</cfif>
</cfloop>
It doesn't work because it seems that the regular expression I used is completely wrong. And also maybe because I duplicate the # sign to deal with coldfusion syntax error
Can anyone has an idea about the regular expression I have to used to deal with this situation?
Thanks in advance
this is an example of an original file
USERID;Post;FirstName;Date1;Mail;Date2;LastName;Telephone
123;Engineer;Alan;"#08/04/14 23:00;08/05/14 23:00#";alan#yahoo.fr;"#10/04/14 11:00;10/05/14 11:00#";Jones;0624262589
parse the original file and replace all occurrences of 0;0 (number;number) with 0,0 (number,number). Then your original solution should work fine.
regex = "/d;/d" to track them down I believe.
Related
OK, so I've been banging my head against this one for a while and getting nowhere. I've been attempting to take the contents of a variable and parse the contained string into parts that would then be ingested into 5 separate variables. Seems simple enough right? Well, it has not proven to be simple at all, at least for me.
So I have a variable (PageContent) that contains the trimmed content from a CFHTTP request.
The PageContent variable now contains:
<tdnowrapalign=right>07/18/2020 13:00</td>
<tdalign=right>1002.12</td>
<tdalign=right>2,874,887</td>
<tdalign=right>12,766</td>
<tdalign=right>13,038</td>
It seems like there should be an easy way to write a loop that would loop over the tags in the "PageContent" variable assigning the content of each tag to a different variable. But every way I try to parse the data in the variable I either get an error (Complex object types cannot be converted to simple values.) or I end up with the content that I originally had in the "PageContent" variable repeated within the loop.
For instance, if I had a loop that would run through 5 iterations and could grab the contents of the tags assigning each to a variable then the desired result would be:
DateTime = "07/18/2020 13:00"
Elevation = "1002.12"
Storage = "2,874,887"
Outflow = "12,766"
Inflow = "13,038"
After trying every example I could find here and elsewhere online I'm now on something like my 100th attempt. Now I'm trying to use regular expressions to grab the contents of the tags and assign them to variables but no luck there. What I ended up with was the entire contents of the PageContent variable being stuffed into each one of the variables. The result was not really unexpected since I don't know of a way to differentiate between the 3 identical "tdalign" tags, but still it seems like at least the first variable would have worked since the tag was different "tdnowrapalign".
<cfset i=5/>
<cfloop index = "LoopCount" from = "1" to = #i#>
<cfif i EQ 1>
<cfset dataDateTime = Replace(PageContent, "<[tdnowrapalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 2>
<cfset elevation = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 3>
<cfset storage = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 4>
<cfset outflow = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 5>
<cfset inflow = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
</cfif>
<cfoutput>
<cfif isdefined("dataDateTime")>
dataDateTime = #dataDateTime#<br>
</cfif>
<cfif isdefined("elevation")>
elevation = #elevation#<br>
</cfif>
<cfif isdefined("storage")>
storage = #storage#<br>
</cfif>
<cfif isdefined("outflow")>
outflow = #outflow#<br>
</cfif>
<cfif isdefined("inflow")>
inflow = #inflow#<br>
</cfif>
</cfoutput>
<cfset i = i - 1>
</cfloop>
Does anyone know if there is a way to get to the desired outcome I described where I end up with 5 variables containing the contents of the tags contained within the "PageContent" variable?
One way of doing it would be like this
<cfset PageContent = '<tdnowrapalign=right>07/18/2020 13:00</td>
<tdalign=right>1002.12</td>
<tdalign=right>2,874,887</td>
<tdalign=right>12,766</td>
<tdalign=right>13,038</td>' />
<cfset data = ListToArray(PageContent, '</td>', false, true) />
<cfset DateTime = ListLast(data[1], '>') />
<cfset Elevation = ListLast(data[2], '>') />
<cfset Storage = ListLast(data[3], '>') />
<cfset Outflow = ListLast(data[4], '>') />
<cfset Inflow = ListLast(data[5], '>') />
Demo: https://trycf.com/gist/b4f3b630bd1cbdc505d07a7d79b68ef5/acf?theme=monokai
I have a set of list values in ColdFusion variable, and I need to replace all the list values into desired text.
For Example:
<cfset headerColumnList = "FirstName,LastName,Email,FrequentGuestID,IP Address,Time Stamp Email Marketing">
<cfset a="test1">
<cfset b="test2">
<cfset c="test3">
<cfset d="test4">
<cfset e="test5">
<cfset f="test6">
<cfloop index = "ListElement" list= "#headerColumnList#" delimiters = ",">
<cfoutput>
#replaceList("#ListElement#","FirstName,LastName,Email,FrequentGuestID,IP Address,Time Stamp Email Marketing","#a#,#b#,#c#,#d#,#e#,#f#",",")#
</cfoutput>
</cfloop>
Output:
test1
test2
test3
test4
test5
Time Stamp test3 Marketing
In the above scenario. The value "Time Stamp Email Marketing" is supposed to be replaced with "test6" but I am getting in an alternative way where it is not replacing the phrase as a whole word. Can anyone tell me how do I replace the list phrases, any alternative for this?
Here you can use the ListQualify function to get exact result of an your scenario. So convert it in to qualify values and looping with that then you can replace it with your own list data. No need to change any order of a list values.
<cfset quoted = listQualify(headerColumnList,"''")>
<cfloop index = "ListElement" list= "#quoted#" delimiters = ",">
#replaceList(ListElement,quoted,"#a#,#b#,#c#,#d#,#e#,#f#")#
<br/>
</cfloop>
The code is working as written. You are seeing this because your check for "Email" in the replaceList() function is firing before the check for "Time Stamp Email Marketing". Notice the word "Email" in that string.
I don't know what your actual use case is but you can change the order of your code for this specific example to make it work like you want.
<cfset headerColumnList = "FirstName,LastName,Email,FrequentGuestID,IP Address,Time Stamp Email Marketing">
<cfset a="test1">
<cfset b="test2">
<cfset c="test3">
<cfset d="test4">
<cfset e="test5">
<cfset f="test6">
<cfloop index = "ListElement" list= "#headerColumnList#" delimiters = ",">
<cfoutput>
#replaceList("#ListElement#","FirstName,LastName,FrequentGuestID,IP Address,Time Stamp Email Marketing,Email","#a#,#b#,#d#,#e#,#f#,#c#",",")#
</cfoutput>
</cfloop>
This gives the desired output. Notice how I reordered the conditions within the replaceList() function.
My CF application provide three selections (semicolon, comma or tab) for users to choose to match the delimiters they have in their file. I want to validate what users selected with what delimiter they have in their file. Is there a way to do this?
So if user is using tab delimiters for his text file but he accidentally selected a comma then I will get this error:
Invalid list index 2.
In function ListGetAt(list, index [, delimiters]), the value of index, 2, is not a valid as the first argument (this list has 1 elements). Valid indexes are in the range 1 through the number of elements in the list.
I think the only way to avoid this type of error is if I can validate user's delimiters being used in their file but I could not find any example when I searched the web.
You didn't specify what kind of data is delimited in the file, so here's just a very simple guessing method:
<!--- read file into memory --->
<cfset fileContent = fileRead( expandPath("yourfile.csv") )>
<!--- declare delimiting characters to check, NOTE: due to using "listLen" you may only specify single characters --->
<cfset possibleDelimiters = [ ";", ",", chr(9) ]> <!--- chr(9) = tab --->
<!--- count number of records found for each delimiter --->
<cfset countResults = {}>
<cfloop array="#possibleDelimiters#" index="delimiter">
<cfset countResults[delimiter] = listLen(fileContent, delimiter)>
</cfloop>
<!--- determine delimiter with the highest count --->
<cfset sortedDelimiters = structSort(countResults, "NUMERIC", "DESC")>
<cfset mostFrequentDelimiter = sortedDelimiters[1]>
<cfoutput>
Is <code>#encodeForHtml(mostFrequentDelimiter)# (#asc(mostFrequentDelimiter)#)</code> the delimiter?
</cfoutput>
However, this will guess terribly if you have text paragraphs in your file due to the frequency of commas in most written languages, so take it with a grain of salt.
Here is my code to output a query to a spreadsheet.
<cfscript>
//Use an absolute path for the files. --->
theDir=GetDirectoryFromPath(GetCurrentTemplatePath());
theFile=theDir & "getTestInv.xls";
//Create an empty ColdFusion spreadsheet object. --->
theSheet = SpreadsheetNew("invoicesData");
//Populate the object with a query. --->
SpreadsheetAddRows(theSheet,getTestInv);
</cfscript>
<cfset format = StructNew()>
<cfset format.dataformat = "#,###0.00">
<cfset SpreadsheetFormatColumn(theSheet,format,10)
<cfspreadsheet action="write" filename="#theFile#" name="theSheet" sheetname="getTestInv" overwrite=true>
The error I am getting is:
Invalid CFML construct found on line 125 at column 32.
ColdFusion was looking at the following text:
,
The CFML compiler was processing:
An expression beginning with /", on line 125, column 30.This message is usually caused by a problem in the expressions structure.
A cfset tag beginning on line 125, column 4.
A cfset tag beginning on line 125, column 4.
125: <cfset format.dataformat = "#,###0.00">
For some reason, it doesn't like the comma, even though it is valid according to the documentation. If I take the comma out, it works, but I need it for the thousands grouping.
Anyone encountered this?
In ColdFusion, the # is a reserved character. To escape it, you'll have to double them up to escape them:
<cfset format.dataformat = "##,######0.00">
Silly that they didn't account for this either in the documentation or followed ColdFusion's formatting rules using 9s instead of #s.
Here is my full working standalone test code:
<cfset myQuery = QueryNew('number')>
<cfset newRow = QueryAddRow(MyQuery, 2)>
<cfset temp = QuerySetCell(myQuery, "number", "349348394", 1)>
<cfset temp = QuerySetCell(myQuery, "number", "10000000", 2)>
<cfscript>
//Use an absolute path for the files. --->
theDir=GetDirectoryFromPath(GetCurrentTemplatePath());
theFile=theDir & "getTestInv.xls";
//Create an empty ColdFusion spreadsheet object. --->
theSheet = SpreadsheetNew("invoicesData");
//Populate the object with a query. --->
SpreadsheetAddRows(theSheet,myQuery,1,1);
</cfscript>
<cfset format = StructNew()>
<cfset format.dataformat = "##,######0.00">
<cfset SpreadsheetFormatColumn(theSheet,format,1)>
<cfspreadsheet action="write" filename="#theFile#" name="theSheet" sheetname="theSheet" overwrite=true>
it should be like that
<cfset format = StructNew()>
<cfset format.dataformat = "##,####0.00">
This has been one of the biggest obstacles in teaching new people ColdFusion.
When to use # is ambiguous at best. Since using them doesn't often create a problem it seems that most people gravitate to using them too much.
So, what are the basic rules?
I think it may be easier to say where NOT to use #. The only place is in cfif statements, and cfset statements where you are not using a variable to build a string in quotes. You would need to use the # sign in almost all other cases.
Example of where you are not going to use it:
<cfset value1 = 5>
<cfset value2 = value1/>
<cfif value1 EQ value2>
Yay!!!
</cfif>
<cfset value2 = "Four plus one is " & value1/>
Examples of where you will use #:
in a cfset where the variable is surrounded by quotes
<cfset value1 = 5>
<cfset value2 = "Four plus one is #value1#"/>
the bodies of cfoutput, cfmail, and cffunction (output="yes") tags
<cfoutput>#value2#</cfoutput>
<cfmail to="e#example.com" from="e#example.com" subject="x">#value2#</cfmail>
<cffunction name="func" output="yes">#value2#</cffunction>
in an attribute value of any coldfusion tag
<cfset dsn = "myDB"/>
<cfquery name="qryUsers" datasource="#dsn#">
<cfset value1 = 5>
<cfset value2 = 10/>
<cfloop from="#value1#" to="#value2#" index="i">
<cfqueryparam value="#value1#" cfsqltype="cf_sql_integer"/>
EDIT:
One oddball little thing I just noticed that seems inconsistent is conditional loops allow the variable name to be used with and without # signs.
<cfset value1 = 5>
<cfloop condition = "value1 LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
<cfset value1 = 5>
<cfloop condition = "#value1# LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
Here's what Adobe has to say about it:
Using number signs
String interpolation:
<cfset name = "Danny" />
<cfset greeting = "Hello, #name#!" />
<!--- greeting is set to: "Hello, Danny!" --->
Auto-escaped string interpolation in cfquery:
<cfset username = "dannyo'doule" ?>
<cfquery ...>
select u.[ID]
from [User] u
where u.[Username] = '#username#'
</cfquery>
<!--- the query is sent to the server (auto-escaped) as: --->
<!--- select u.[ID] from [User] u where u.[Username] = 'dannyo''doule' --->
<!--- note that the single-quote in the username has been escaped --->
<!--- by cfquery before being sent to the database server --->
Passing complex arguments/attributes in CFML:
<cfset p = StructNew() />
<cfset p.firstName = "Danny" />
<cfset p.lastName = "Robinson" />
<cfmodule template="modules/view/person.cfm" person="#p#">
<!--- the variable Attributes.person will be --->
<!--- available in modules/view/person.cfm --->
Passing complex arguments requires # signes only in CFML, not CFScript. In addition, you can pass any kind of value: simple values, arrays, structs, cfcomponents, cffunctions, java objects, com objects, etc.
In all these cases, the text between the # signs does not have to be the name of a variable. In fact, it can by any expression. Of course, for string interpolation, the expression must evaluate to a simple value, but for argument/attribute passing in CFML, the expression may evaluate to any complex value as well.
The #...# syntax allows you to embed an expression within a string literal. ColdFusion is unfortunately pretty inconsistent about what's a string and what's an expression. Jayson provided a good list of examples of when to use (or not use) #s.
At the risk of sounding like a wise-guy, a rule of thumb is: use # around variables or expressions only when not doing so doesn't yield the correct result. Or: if you don't need them, don't use them.
I like Jayson's answer though.
Let's start by assuming you aren't talking about cfoutput tags, cause then the answer is always, elsewhere in your code, if you are inside of quotation marks, then need to use # symbols if it's possible to actually type the value that is going to be used...so if you are in a cfloop tag setting the 'to' attribute, you could easily type 6, but if you want to use a variable you need to use the # symbols. Now if you are in a cfloop tag setting the query parameter, there is no way you could actually type the query into that attribute, there is no way to type a query, so no # symbols are needed.
Likewise in a cfdump tag, you can dump static text, so if you want to dump the contents of a variable, then you will need to use a # symbol. This problem is generally self-correcting, but I feel your pain, your students are probably frustrated that there is no "ALWAYS USE THEM" or "NEVER USE THEM" approach...sadly this isn't the case, the only thing that is true, is only one way inside of quotation marks is going to be correct. So if it isn't working look at it hard and long and think to yourself: "Could I type that value out instead of using the value contained in that variable?" If the answer is no, then the # symbols won't be needed, otherwise get your # character foo on.