Do you really think that after 15 years, "Ignoramus" is still waiting
for a reply to this posting?
As for parsing csv, just use the Tcllib csv module, as it has already
been debugged over the ensuing 15 years and now should handle most all
of the odd edge cases in the CSV format.
http://tmml.sourceforge.net/doc/tcllib/csv.html
Now, some critiques of your code:
First - the snippet you posted does not run, due to syntax errors:
$ ./usenet-csv
missing close-brace
while executing
"foreach line $lines {"
(file "./usenet-csv" line 4)
Second - this part:
> set lines [split [exec cat file.csv] \n]
is a non-Tcl, non-portable way to read in the CSV file (it also
may or may not handle character encodings properly depending on the
rest of your system settings).
Third - for this part:
> eval lappend lineList [split [string trim $unquoted ,] ,]
eval is almost never necessary in modern Tcl. And for the few times it
is, you have to be careful to properly quote evaled variables lest you
open yourself up to Tcl injection attacks. This line should likely
work properly without the eval, as you are simply doing an lappend.
Fourth - here:
> set lineList {}
You reset lineList in the middle of the line reading loop, which forces
use of lineList before the loop iterates, otherwise only the last line
remains in the list when your outer loop terminates.
So, I created this bit of code to create a csv file:
#!/usr/bin/tclsh
package require csv
set fd [open file.csv {WRONLY CREAT TRUNC}]
# embedded " within a field
puts $fd [csv::join [list "col 1" "col 2" "col \"3\"" "col 4"]]
# embedded , within a field
puts $fd [csv::join [list c1 c2 c,3 c4]]
# both " and , embedded within a field
puts $fd [csv::join [list c1 c\",2 c3 c4]]
# newline within a field
puts $fd [csv::join [list c1 c2 c\n3 c4]]
close $fd
Running it creates this as "file.csv":
col 1,col 2,"col ""3""",col 4
c1,c2,"c,3",c4
c1,"c"",2",c3,c4
c1,c2,"c
3",c4
And I created this, using the Tcllib module, to show that the Tcllib
module correctly reads this file back:
#!/usr/bin/tclsh
package require csv
package require struct::queue
struct::queue q
set fd [open file.csv RDONLY]
csv::read2queue $fd q
set seq 0
foreach row [q get [q size]] {
puts "Row [incr seq]: $row"
}
close $fd
Running it results in:
Row 1: {col 1} {col 2} {col "3"} {col 4}
Row 2: c1 c2 c,3 c4
Row 3: c1 c\",2 c3 c4
Row 4: c1 c2 {c
3} c4
Which is a correct result.
Correcting your syntax error and appending a "puts [join $lineList]" to
the end of your code, then running it, results in only this for the
same input .csv file:
3 ,c4
Moving the "puts [join $lineList]" to inside your foreach loop (and
changing it to "puts "Row [incr seq]: $lineList" to get a "Row 1:"...
indicator) results in:
Row 1: {col 1} {col 2} {col } 3 {} {col 4}
Row 2: c1 c2 c,3 c4
Row 3: c1 c ,2 c3 c4
Row 4: c1 c2 c
Row 5: 3 ,c4
Which is an incorrect result. The first and third rows have had an
extra column falsely inserted, all of the embedded quotes have been
stripped away, the fourth row has been split into two separate rows,
and the false fifth row has had an extra comma inserted that does not
belong.
Moral: Just use the existing module. The CSV format has enough sharp
edge cases that it is by far more difficult to parse than it at first
appears. The existing module's already been through having to handle
the sharp edge cases, take advantage of that history.