Reputation: 5459
I'm having a Csv file like below:
Production,FALSE,Other Line,Release,UOF-919 BASE,3A001A11,9X999,PC,"Jap,Ind",006
And i'm reading and setting the values to a variable using tcl like below:
set fileIn [open "C:/myfile.csv" r]
while {[gets $fileIn sLine] >= 0} {
set lsLine [split $sLine ","]
set sType "Hardware"
set sName [lindex $lsLine 1]
set Sdev [lindex $lsLine 2]
set spara [lindex $lsLine 3]
set sDescription [lindex $lsLine 4]
set sManage [lindex $lsLine 5]
set sconnect [lindex $lsLine 6]
set sUOM [lindex $lsLine 7]
set sCountry [lindex $lsLine 8]
#my operations
}
flush $fileId
close $fileId
}
Here i'm not able to set "Jap,Ind" to sCountry because it already has one more comma inside the quotes. Can anybody help me to set that? I'm new in TCL.
Upvotes: 0
Views: 516
Reputation: 71538
You can use the csv
package (it is a package that has been included in the default libraries for a while now):
set fileIn [open "C:/myfile.csv" r]
package require csv
while {[gets $fileIn sLine] >= 0} {
set lsLine [::csv::split $sLine] # I'd use the -alternate
# switch if you can have empty elements
set sType "Hardware"
set sName [lindex $lsLine 0]
set Sdev [lindex $lsLine 1]
set spara [lindex $lsLine 2]
set sDescription [lindex $lsLine 3]
set sManage [lindex $lsLine 4]
set sconnect [lindex $lsLine 5]
set sUOM [lindex $lsLine 6]
set sPin [lindex $lsLine 7]
set sCountry [lindex $lsLine 8]
#my operations
}
flush $fileId
close $fileId
Note that I also changed the indices. Tcl lists are 0-based, meaning that the first element of a list has the index 0. [lindex $lsLine 0]
thus gives the first element from the list $lsLine
.
And maybe if you want to make the code shorter, you could use lassign
(as of Tcl 8.5)
set fileIn [open "C:/myfile.csv" r]
package require csv
while {[gets $fileIn sLine] >= 0} {
set lsLine [::csv::split $sLine]
set sType "Hardware"
lassign $lsLine sName Sdev spara sDescription sManage sconnect sUOM sPin sCountry
#my operations
}
flush $fileId
close $fileId
Alternate solution if csv is not available which works for most cases (Tcl 8.6):
set lsLine [lmap {a b} [regexp -all -inline -- {("[^\"]+"|[^,]*)(?:$|,)} $sLine] {set b}]
Tcl 8.5:
set matches [regexp -all -inline -- {("[^\"]+"|[^,]*)(?:$|,)} $sLine]
set lsLine {}
foreach {a b} $matches {lappend lsLine $b}
The \"
can be replaced with a simple "
but I usually insert it if there's an issue with the code editor's syntax highlighting.
For more complex cases where escaped characters with a backslash can be involved (Tcl 8.6):
set lsLine [lmap {a b} [regexp -all -inline -- {("(?:\\.|[^\"])+"|(?:\\.|[^,])*)(?:$|,)} $sLine] {set b}]
Tcl 8.5:
set matches [regexp -all -inline -- {("(?:\\.|[^\"])+"|(?:\\.|[^,])*)(?:$|,)} $sLine]
set lsLine {}
foreach {a b} $matches {lappend lsLine $b}
Upvotes: 2
Reputation: 13252
package require csv
set fileIn [open C:/myfile.csv r]
while {[gets $fileIn sLine] >= 0} {
set lsLine [csv::split $sLine]
lassign $lsLine - sName Sdev spara sDescription sManage sConnect sUOM sCountry
}
close $fileId
}
Never try to parse CSV data with split
. It will end in tears.
Note that this assumes that you want to assign to sName
from index 1, i.e. the second element. The first element is assigned to the dummy variable -
.
For sparse assignment, you can either use (assuming you want #8 rather than #9)
lassign $lsLine - - Sdev - - sManage sConnect sUOM sCountry
or
foreach idx {2 5 6 7 8} name {Sdev sManage sConnect sUOM sCountry} {
set $name [lindex $lsLine $idx]
}
If you don't have csv
installed, you can use teacup install csv
from the command line to get it.
Documentation: close, csv package, gets, lassign, open, package, set, while
Upvotes: 1
Reputation: 16428
Since you are using split
to extract the variable (yes, your input is based on the comma, so we obviously go to that approach), the input's value should not have a 'comma' in it. To avoid that, we can replace it for a while and revert back wherever needed.
set fileIn [open "file.csv" r]
while {[gets $fileIn sLine] >= 0} {
# Replacing the 'comma' with 'colon' and saving it into the save variable
regsub {"(.*?),(.*?)"} $sLine {\1:\2} sLine
set lsLine [split $sLine ","]
# Your other indices can be processed and saved here
# Getting the country values
set sCountry [lindex $lsLine 8]; # Yes, the country value is available in '8th' index only, not on 9th. (Index starts with '0')
# Replacing the 'colon' with 'comma' back again
regsub : $sCountry , sCountry
puts "Country Value : $sCountry"
}
close $fileIn
Upvotes: 1