loading .json file in tcl - json

I have quite a large (30 kb) .json file, that I want to read inside a script. Till now I just copied the content of the file in my script, but that leads to very ugly code.
This is the code
set allAttrib { <copyOfContent>)
set allAttrib_d [::json::json2dict $allAttrib]
I am sure this should not be that hard, but I do not manage to find an answer somewhere.
Thanks in advance

You need to open and read the file to a string.
set fp [open "somefile" r]
set json_string [read $fp]
close $fp
set allAttrib_d [::json::json2dict $json_string]
https://wiki.tcl-lang.org/page/How+do+I+read+and+write+files+in+Tcl

Related

how to copy a row in a .csv file into a column in another .csv file in tcl ?

I wish to copy a specific row in a .csv file to a specific column in another .csv file using tcl.
What i've tried is to copy the row i wanted into a new .csv file and then copy this row manually into my .csv file. But I wish to automate all this and directly copy a row in the .csv into a column in an existing .csv file.
Here is what i tried:
package require csv
set fp [open "filenameSource.csv" r]
set secondColumnData {}
while {[gets $fp line]>=0} {
if {[llength $line]>0} {
lappend secondColumnData [lindex [split $line ","] 1]
}
}
close $fp
puts $secondColumnData
set filename "Destination.csv"
set fileId [open $filename "w"]
puts -nonewline $fileId $secondColumnData
close
Is there a way to have a pointer at row x in the source file and copy it into a specific destination into the Destination file.
I am new to tcl. Please provide example.
Thanks,
IEK
One thing you'll need to learn as a newcomer to Tcl is that there's a lot of useful code in Tcllib, a suite of packages written by the Tcl community. In this case, the csv and struct::matrix packages make this task trivial (as I understand it), which is great because CSV files have some tricky aspects that aren't obvious.
package require csv
package require struct::matrix
# Read the source data
set srcMatrix [struct::matrix]
set f [open "filenameSource.csv" r]
csv::read2matrix $f $srcMatrix
close $f
# Read the destination data so we can UPDATE it
set dstMatrix [struct::matrix]
set f [open "Destination.csv" r+]
csv::read2matrix $f $dstMatrix
# Leaving the file open; we're going to rewrite it…
# Do the copying operation; I assume you know which row and column to copy from/to
$dstMatrix set column 2 [$srcMatrix get row 34]
# Write back
chan seek $f 0
csv::writematrix $f $dstMatrix
chan truncate $f; # Make sure there's no junk left if the file shortened
close $f

Binary format issue when writing to file

Below there is a trivial script writing a byte to a file:
set wf [open "test.bin" "w"]
set int_result 0x80
puts -nonewline $wf [binary format c [expr {$int_result}]]
close $wf
exit
Surprisingly, the file will contain 0x3F, not 0x80.
Can anybody explain what is happening?
Thanks a lot,
Dmitry
Tcl prefers to read and write text files by default, which means it does a number of transformations on the data for you both going into Tcl and going out to a file. When working with binary data, you want these switched off. Either use wb instead of w in the call to open (just like in C stdio's fopen() call), or use fconfigure to put the channel into binary mode after opening it.
set wf [open "test.bin" wb]
set wf [open "test.bin" w]
# It's the -translation option for historical/backward-compatibility reasons
fconfigure $wf -translation binary
They're equivalent; the b makes open call (the implementation of) fconfigure internally to set exactly that option.
Quick fix: use
open test.bin wb
to open the file in binary mode.

how to read the binary section of script currently being evaluated?

How do I read the section after end-of-stream (^Z) in a Tcl-script being sourced?
So far I got info script returning the filename of the currently sourced script which I could open just like any file and put the read position to after end-of-stream by just parsing the file.
In theory the content of the file could change between the invocation of source and subsequent info script and open, possibly causing temporal inconsistency between read script and binary data.
Is there a magic command for this that I've missed? Or do we rely on users/administrators making sure such inconsistencies can't happen?
Suggestion
Provide for your custom source that extracts the trailer in the same I/O step as sourcing the contained script. For example:
interp hide {} source source
proc ::source {fp} {
set size [file size $fp]
set chan [open $fp r]
info script $fp
try {
chan configure $chan -eofchar {\u001a {}}
set script [read $chan]
uplevel 1 [list eval $script]
set scriptOffset [chan tell $chan]
if {$scriptOffset < $size} {
chan seek $chan 1 current; # move cursor beyond eof
chan configure $chan -translation binary
set trailer [read $chan]
# do whatever you want to do with the trailer
}
} finally {
close $chan
}
}
Some remarks
The trick is to employ the same machinery as Tcl's source does internally: configure -eofchar.
Once it has been determined, that there is a trailer (i.e., content beyond the eof char), seek is used to position the cursor at the script's offset.
A second read will then get you the trailer.
From this point onwards, you must be careful to maintain the trailer value in its shape as byte array.
Disclaimer: Tcl wizards like Donal might have better ways of doing so. Also, single-file distribution mechanisms like starkits might have helpers for dealing with script trailers.

How to map a file handle to the file it is accessing

I am trying to map a file handle like file6
(the result of a previous set fh [open somefile.txt w]) to the file it was accessing.
So I'd like a mapping between file6 --> somefile.txt
I tried file channels, but this only lists the channel names - not the actual file name.
As you've noticed, Tcl doesn't keep this information for you, but you can easily keep track of it for yourself by doing something like this:
set fh [open somefile.txt w]
set filenames($fh) somefile.txt
Then when you want to know the associated file name, you can
puts $filenames($fh)
You can automate this by e.g.:
proc myOpen {name args} {
global filenames
set fh [open $name {*}$args]
set filenames($fh) $name
return $fh
}
This is of course a quick-and-dirty semi-solution that leaves some important things open, like for instance how the association needs to be similarly removed if the channel is closed. It is possible, but a bit complicated, to create a more comprehensive solution.
Documentation:
global,
open,
proc,
puts,
return,
set,
{*} (syntax)

in tcl, how to edit string in the open file?

let's say that I have opened a file using:
set in [open "test.txt" r]
I'm intend to revise some string in the certain line, like:
style="fill:#ff00ff;fill-opacity:1"
and this line number is: 20469
And I want to revise the value ff00ff to other string value like ff0000.
What are the proper ways to do this? Thanks in advance!
You need to open the file in read-write mode; the r+ mode is probably suitable.
In most cases with files up to a reasonable number of megabytes long, you can read the whole file into a string, process that with a command like regsub to perform the change in memory, and then write the whole thing back after seeking to the start of the file. Since you're not changing the size of the file, this will work well. (Shortening the file requires explicit truncation.)
set f [open "test.txt" r+]
set data [read $f]
regsub {(style="fill:#)ff00ff(;fill-opacity:1)"} $data {\1ff0000\2} data
seek $f 0
puts -nonewline $f $data
# If you need it, add this here by uncommenting:
#chan truncate $f
close $f
There are other ways to do the replacement; the choice depends on the details of what you're doing.