I am a new tcl user.
I am trying to type white space for output file.
I am using the lappend command:
lappend lineM1 [ format "%-4s" " " ]
set outfile [open "test" w]
puts $outfile "$lineM1"
close $outfile
This is what I get:
{ }
How can I remove the brackets? How do I print just white space?
I'm not really sure about what you want to do, but if you replace the
lappend with a set lineM1. You don't get the brackets.
The tcl command lappend is a list command. It appends the result of format "%-4s" " " to the not yet existing list lineM1.
By the way, if you just want to print white space into the $outfile you don't need to format that white space. Perhaps you should have a look at the format manual page.
Example:
set foo "bar"
puts $foo
Will result in:
bar
In your case you would add the file handle $outfile to the puts call:
puts $outfile $foo
The tcl.tk wiki is a good resource for starters.
Related
I have written the script below, and for some reason it output an extra 3rd line (top out.txt & name ). How do I stop it from outputing the last 2 line to the output file.
I think is because of this line but not too sure: set lines [split [read $list] "\n"]
Input put file - a.txt
**********************
abc
bcd
Output
*******
top out.txt
name abc
top out.txt
name bcd
top out.txt
name
set outfile [open "out.txt" w]
set list [open "a.list" r]
set dir "[glob a.txt]"
foreach ddd $dir {
set lines [split [read $list] "\n"]
foreach line $lines {
puts $outfile "top $dir"
puts $outfile "name $line"
}
}
close $list
close $outfile
The core of the issue is that split works with field separators and not field terminators; this is where you see the difference between the two.
If we look at the characters of your file, we'll see this:
a b c \n b c d \n. (Technically, the \n might instead be \r \n or \r; Tcl auto-adapts to that by default for text files.)
If we split those characters at the \n, we get these substrings: abc, bcd, and the empty string for the characters after that final \n. This means that when we join the strings with \n (as the joiner string) between all the elements, we get the original string back again.
You've got a few options for dealing with this. My favourite is to add code to ignore blank lines; versions of this are particularly useful for all sorts of human-written lists (as it's fairly easy to also add in comment skipping):
set lines [split [read $list] "\n"]
foreach line $lines {
if {$line eq ""} continue
puts $outfile "top $dir"
puts $outfile "name $line"
}
Another option is to use the -nonewline option to read to discard that final \n:
set lines [split [read -nonewline $list] "\n"]
foreach line $lines {
puts $outfile "top $dir"
puts $outfile "name $line"
}
You could also string trimright before splitting, but that would strip multiple newlines from the end (if they're present).
set lines [split [string trimright [read $list] "\n"] "\n"]
foreach line $lines {
puts $outfile "top $dir"
puts $outfile "name $line"
}
Finally, if you're using a machine-generated list, you could consider just getting that code to not put a newline at the end. Sometimes that's the easiest option of all.
I have file with the below lines (file.list):
insert_buffer [get_ports { port }] BUFF1 -new_net net -new_cell cell
I'm reading the file with the below script (read.tcl):
#! /usr/local/bin/tclsh
foreach arg $argv {
set file [open $arg r]
set data [ read $file ]
foreach line [ split $data "\n" ] {
puts $line
set name [lindex $line [expr [lsearch -all $line "-new_cell"]+1]]
puts $name
}
close $file
}
while running the above script (read.tcl file.list) I get error since I have "[" in file.list and script think its a beginning of TCL command.
list element in braces followed by "]" instead of space
while executing
"lsearch -all $line "-new_cell""
("foreach" body line 5)
invoked from within
"foreach line [ split $data "\n" ] {
How can I read the file correctly and overcome the "[" symbol?
How can I read the file correctly and overcome the "[" symbol?
I don't really understand why you are doing what you are doing (processing one Tcl script by another), but you have to make sure that each line is a valid Tcl list before submitting it to lsearch.
lsearch -all [split $line] "-new_cell"
Only split will turn an arbitrary string (containing characters special to Tcl) into a valid Tcl list.
This is one of the few times in Tcl that you need to worry about what type of data you have. $line holds a string. Don't use list commands on strings because there's no guarantee that an arbitrary string is a well-formed list.
Do this:
set fields [split $line]
# don't use "-all" here: you want a single index, not a list of indices.
set idx [lsearch -exact $fields "-new_cell"]
if {$idx == -1} {
do something here if there's no -new_cell in the line
} else {
set name [lindex $fields $idx+1]
}
In order to apply a list operation on the variable, it has to be a valid list. The variable $line is not a valid list.
It is better to use regexp rather than lsearch
regexp -- {-new_cell\s+(\S+)} $x match value
puts $value
Output :
cell
Below code, append can add all file paths to a single line, but there is no space between them.
How to add a space between each path?
set all_path ""
foreach line $lines {
set filepath [proc_get_file_path $line]
...
#some commands
...
append ::all_path $filepath
}
Expected output:
../path/a ../path/b ../path/c ...
How do you want to use all_path later on?
From the distance, this is where you would like to use a Tcl list:
set all_path [list]
foreach line $lines {
set filepath [proc_get_file_path $line]
# ...
lappend all_path $filepath
}
The string representation of Tcl lists would also match your expectation re a whitespace delimiter. You can also assemble such a string manually, with append introducing a whitespace explicitly: append all_path " " $filepath. But maybe, this is not what you want to begin with ...
I have two variables, let's say $a with the list name & $b with age, when I try to redirect into csv, it creates csv but both $a & $b are falling into same same row instead of different, How can i separate them into two different rows?
puts $outfile "$b_1 \t$c"
set outfile [open "result_table_sort.csv" w+]
The recommended way of creating a CSV file is with the csv package in Tcllib. It handles all sorts of tricky edge cases for you so you don't have to.
package require csv
set outfile [open "thing.csv" w]
foreach aRow $a bRow $b {
puts $outfile [csv::join [list $aRow $bRow]]
}
close $outfile
You can switch to producing tab-separated output by passing \t as the separator character to csv::join:
puts $outfile [csv::join [list $aRow $bRow] "\t"]
Also consider using the csv::joinlist and csv::joinmatrix commands. They both let you build an entire table in one go, the first as a list of lists (list of rows, each row containing a list of columns) and the second as a matrix (structure defined in struct::matrix package in Tcllib).
Found the solution by putting comma in between two variable solved my issue....
puts $outfile "$b_1,\t$c"
set outfile [open "result_table_sort.csv" w+]
I have a file in here which has multiple set statements. However I want to extract the lines of my interest. Can the following code help
set in [open filename r]
seek $in 0 start
while{ [gets $in line ] != -1} {
regexp (line to be extracted)
}
Other solution:
Instead of using gets I prefer using read function to read the whole contents of the file and then process those line by line. So we are in complete control of operation on file by having it as list of lines
set fileName [lindex $argv 0]
catch {set fptr [open $fileName r]} ;
set contents [read -nonewline $fptr] ;#Read the file contents
close $fptr ;#Close the file since it has been read now
set splitCont [split $contents "\n"] ;#Split the files contents on new line
foreach ele $splitCont {
if {[regexp {^set +(\S+) +(.*)} $ele -> name value]} {
puts "The name \"$name\" maps to the value \"$value\""
}
}
How to run this code:
say above code is saved in test.tcl
Then
tclsh test.tcl FileName
FileName is full path of file unless the file is in the same directory where the program is.
First, you don't need to seek to the beginning straight after opening a file for reading; that's where it starts.
Second, the pattern for reading a file is this:
set f [open $filename]
while {[gets $f line] > -1} {
# Process lines
if {[regexp {^set +(\S+) +(.*)} $line -> name value]} {
puts "The name \"$name\" maps to the value \"$value\""
}
}
close $f
OK, that's a very simple RE in the middle there (and for more complicated files you'll need several) but that's the general pattern. Note that, as usual for Tcl, the space after the while command word is important, as is the space between the while expression and the while body. For specific help with what RE to use for particular types of input data, ask further questions here on Stack Overflow.
Yet another solution:
as it looks like the source is a TCL script, create a new safe interpreter using interp which only has the set command exposed (and any others you need), hide all other commands and replace unknown to just skip anything unrecognised. source the input in this interpreter
Here is yet another solution: use the file scanning feature of Tclx. Please look up Tclx for more info. I like this solution for that you can have several scanmatch blocks.
package require Tclx
# Open a file, skip error checking for simplicity
set inputFile [open sample.tcl r]
# Scan the file
set scanHandle [scancontext create]
scanmatch $scanHandle {^\s*set} {
lassign $matchInfo(line) setCmd varName varValue; # parse the line
puts "$varName = $varValue"
}
scanfile $scanHandle $inputFile
close $inputFile
Yet another solution: use the grep command from the fileutil package:
package require fileutil
puts [lindex $argv 0]
set matchedLines [fileutil::grep {^\s*set} [lindex $argv 0]]
foreach line $matchedLines {
# Each line is in format: filename:line, for example
# sample.tcl:set foo bar
set varName [lindex $line 1]
set varValue [lindex $line 2]
puts "$varName = $varValue"
}
I've read your comments so far, and if I understand you correctly your input data file has 6 (or 9, depending which comment) data fields per line, separated by spaces. You want to use a regexp to parse them into 6 (or 9) arrays or lists, one per data field.
If so, I'd try something like this (using lists):
set f [open $filename]
while {[gets $f line] > -1} {
# Process lines
if {[regexp {(\S+) (\S+) (\S+) (\S+) (\S+) (\S+)} $line -> name source drain gate bulk inst]} {
lappend nameL $name
lappend sourceL $source
lappend drainL $drain
lappend gateL $gate
lappend bulkL $bulk
lappend instL $inst
}
}
close $f
Now you should have a set of 6 lists, one per field, with one entry in the list for each item in your input file. To access the i-th name, for example, you grab $nameL[$i].
If (as I suspect) your main goal is to get the parameters of the device whose name is "foo", you'd use a structure like this:
set name "foo"
set i [lsearch $nameL $name]
if {$i != -1} {
set source $sourceL[$i]
} else {
puts "item $name not found."
set source ''
# or set to 0, or whatever "not found" marker you like
}
set File [ open $fileName r ]
while { [ gets $File line ] >= 0 } {
regex {(set) ([a-zA-Z0-0]+) (.*)} $line str1 str2 str3 str4
#str2 contains "set";
#str3 contains variable to be set;
#str4 contains the value to be set;
close $File
}