mike234
mike234

Reputation: 3

TCL read in a structured file and enter contents into an array

I am using TCL to read in a file of the form:

string1, 1.2 3.4

string2, 5.6 7.8

string3, etc...

The code needs to assume that the values of the key are not known in advance of reading in the file

I need the file to end up in an array in order to keep in sync with some other existing code.

I have tried

array set FileA
set libDir "/tmp/"

set Datafile [open $libDir\Data.csv RDONLY]

set DataFileB [split $Datafile "\n"]

foreach line $DatafileB {

    [lappend FileA [split $line ","]]

)

## also tried:

while {![eof $Datafile]} {
    set DatafileB [string trim [gets $Datafile]]
    puts -nonewline "'$DatafileB'"
    if {$DatafileB == {}} {continue}
    puts "EMFLimits contents are $DatafileB "
    set FileA [split $DatafileB ","]
    foreach {i j} $FileA {}
    set key $i
    set val $j
    set FileAurrents [split [gets [open $libDir\EFM_CurrentLimit.csv RDONLY]]]
    if {[info exists FileA($key)]} {
        lappend FileA($key) $val
    } else {
        set FileA($key) [list $val]
    }
    }

I keep getting an empty array?

I am quite new to TCL and have been tripped up on this simple puzzle for a few weeks now (in amongst other work too).

Upvotes: 0

Views: 1886

Answers (1)

Jerry
Jerry

Reputation: 71578

I don't quite understand what you are trying to do, but if you want to fill an array with the contents of your file, then there are a few things wrong with both your attempts.

Your first attempt:

array set FileA    ;# This should have raised an error without the empty list
array set FileA {} ;# You can create a new array like this
set libDir "/tmp/"

set Datafile [open $libDir\Data.csv RDONLY]

set DataFileB [split $Datafile "\n"]  ;# open creates a channel for the file. If you do 
                                       # puts $Datafile, you will see something 
                                       # like file225721ca0b0 instead of the actual 
                                       # contents, so you won't get anything useful here

set fileContents [read $Datafile] ;# This gets all the contents of the file

close $Datafile                   ;# Closing a channel after use is a good practice

set DataFileB [split $fileContents "\n"] ;# And now we split

foreach line $DataFileB {             ;# Variable names are case sensitive. Be consistent
    [lappend FileA [split $line ","]] ;# You cannot use lappend on an array. In Tcl, there
                                       # is a difference between arrays and lists. 
                                       # lappend essentially stands for list append. 
                                       # This line should raise an error

    # You can use a bit of the code you used in your second attempt:
    foreach {i j} [split $line ","] {}
    set key $i
    set val $j
    if {[info exists FileA($key)]} {
        lappend FileA($key) $val
    } else {
        set FileA($key) [list $val]
    }
} ;# How did this become a parenthesis? It should be a brace

parray FileA   ;# You can use this to pretty print the contents of the array

As for your second attempt

while {![eof $Datafile]} {
    set DatafileB [string trim [gets $Datafile]]
    puts -nonewline "'$DatafileB'"
    if {$DatafileB == {}} {continue}
    puts "EMFLimits contents are $DatafileB "
    set line [split $DatafileB ","]  ;# FileA is an array you created. This line should 
                                      # raise an error because you are trying to overwrite 
                                      # the array with a "standard value". Use a different 
                                      # name. I will use line
    foreach {i j} $line {}
    set key $i
    set val $j
    set FileAurrents [split [gets [open $libDir\EFM_CurrentLimit.csv RDONLY]]] ;# You are 
            # not using this variable, so I don't know what you intend to do, but his line 
            # will continuously create new channels of the same file. You need to close 
            # channels after using them using close
    if {[info exists FileA($key)]} {
        lappend FileA($key) $val
    } else {
        set FileA($key) [list $val]
    }
}

close $Datafile
parray FileA

That said, I would probably write something more like this:

array set fileA {}
set libDir "tmp"
set dataFile [open [file join $libDir Data.csv] r]
set fileContents [read $dataFile]
close $dataFile

foreach line [split $fileContents "\n"] {
    lassign [split $line ","] key val
    lappend fileA($key) $val          ;# lappend creates the variable if it does not 
                                       # already exists so it is pretty convenient
}

parray fileA

You probably should do something similar with your other file and get another array, then compare the two, or whatever it is you are trying to do with the two files.

Upvotes: 1

Related Questions