In TCL I am writing the regular expression for below output:
Output args is
packet-filter 0
identifier 0
direction bidirectional
network-ip 10.7.98.231/32
ue-port-start 0
ue-port-end 0
nw-port-start 0
nw-port-end 0
protocol 1
precedence 0
packet-filter 1
identifier 1
direction uplink
network-ip 10.7.98.231/32
ue-port-start 0
ue-port-end 0
nw-port-start 0
nw-port-end 0
protocol 1
precedence 0
Output of my Regular Expression : regexp -all -inline {direction\s+(\S+)} $args is
{direction bidirectional} bidirectional {direction uplink} uplink
I need to extract the direction value which is bidirectional and uplink
Any suggestion ?
For the current case, where the captured substrings are chunks of non-whitespace text, you may re-build the output checking if each item has length set to 1:
set results [regexp -all -inline {direction\s+(\S+)} $args]
set res {}
foreach item $results {
if {[llength $item] == 1} {
lappend res $item
}
}
Then, $res will only hold bidirectional and uplink.
See the Tcl demo.
For a more generic case, you may use
set res {}
foreach {whole capture1} $results {
lappend res $capture1
}
See this Tcl demo
You may add more captureX arguments to accommodate all the capturing group values returned by your regex.
You simply need a loop or something equivalent. If you need to work on each direction individually, a foreach loop is appropriate:
set results [regexp -all -inline {direction\s+(\S+)} $args]
foreach {main sub} $results {
puts $sub
}
# bidirectional
# uplink
Or if you need the list of directions, then lmap sounds appropriate:
set directions [lmap {main sub} $results {set sub}]
# bidirectional uplink
The regexp is not absolutely necessary, you may process the value of args into a dictionary:
set d [dict create]
foreach {k v} $args {
dict lappend d $k $v
}
puts [dict get $d direction]
Related
I wondered if I could get some advice, I'm trying to create a list by reading a file with input shown below
Example from input file
Parameter.Coil = 1
Parameter.Coil.Info = Coil
BaseUTC.TimeSet_v0 = 1
BaseUTC.TimeSet_v0.Info = TimeSet_v0
BaseUTC.TimeSet_v1 = 1
BaseUTC.TimeSet_v1.Info = TimeSet_v1
BaseUTC.TimeSet_v14 = 1
BaseUTC.TimeSet_v14.Info = TimeSet_v14
BaseUTC.TimeSet_v32 = 1
BaseUTC.TimeSet_v32.Info = TimeSet_v32
VC.MILES = 1
VC.MILES.Info = MILES_version1
I am interested in any line with prefix of "BaseUTC." and ".Info" and would like to save value after "=" in a list
Desired:
output = TimeSet_v0 TimeSet_v1 TimeSet_v14 TimeSet_v32
I've tried the following but not getting the desired output.
set input [open "[pwd]/Data/Input" r]
set list ""
while { [gets $input line] >= 0 } {
incr number
set sline [split $line "\n"]
if {[regexp -- {BaseUTC.} $sline]} {
#puts "lines = $sline"
if {[regexp -- {.Info} $sline]} {
set output [lindex [split $sline "="] 1]
lappend list $output
}}}
puts "output = $list"
close $input
I get output as
output = \ TimeSet_v0\} \ TimeSet_v1\} \ TimeSet_v14\} \ TimeSet_v32\}
Can any help identify my mistake, please.
Thank you in advance.
A lot of your code doesn't seem to match your description (Steering? I thought you were looking for BaseUTC lines?), or just makes no sense (Why split what you already know is a single line on newline characters?), but one thing that will really help simplify things is a regular expression capture group. Something like:
#!/usr/bin/env tclsh
proc process {filename} {
set in [open $filename]
set list {}
while {[gets $in line] >= 0} {
if {[regexp {^BaseUTC\..*\.Info\s+=\s+(.*)} $line -> arg]} {
lappend list $arg
}
}
close $in
return $list
}
puts [process Data/Input]
Or using wildcards instead of regular expressions:
proc process {filename} {
set in [open $filename]
set list {}
while {[gets $in line] >= 0} {
lassign [split $line =] var arg
if {[string match {BaseUTC.*.Info} [string trim $var]]} {
lappend list [string trim $arg]
}
}
close $in
return $list
}
I am a beginner in TCL scripting. I am working on parsing the data from a Verilog file to a xls file as below.
Input Verilog file contains following data:
Inferred components
Operator Signedness Inputs Outputs CellArea Line Col Filename
=====================================================================================================
apn
u_apn_ttp_logic
abc
u_apn_wgt_op_rd_u_apn_sca_u_part_sel1_sl_69_33
module:shift_right_vlog_unsigned_4662_7709
very_fast/barrel >> x 25x5 25 223.02 69 33 part_select.v
=====================================================================================================
apn
u_apn_ttp_logic
u_apn_wgt_op_rd_u_apn_scale_u_part_sel1_sub00283545
module:sub_signed_4513_5538
very_fast - signed 11x24 25 152.80 0 0 a
=====================================================================================================
(This is a long file…)
The parsing will end after the last section:
=====================================================================================================
apn
u_apn_start_ctrl_final_adder_add_212_41
module:add_unsigned_carry_882
very_fast + unsigned 32x32x1 32 120.39 212 41 feu_start_ctrl.v
=====================================================================================================
I want to extract the data as below , consider first section
Top name=apn
Instance=u_apn_ttp_logic/abc/u_apn_wgt_op_rd_u_feu_scale_u_part_select1_srl_69_33
Module = shift_right_vlog_unsigned_4662_7709
Architecture=very_fast/barrel
Operator = >>
Sign=x
Input Size = 25x5
Output = 25
Area = 223.02
Column = 69
Row = 33
File Name = part_select.v
I am stucked at a point while implementing this.
below is my approach for the same:
set fd "[open "path_data.v" r]"
set flag 0
while {[gets $fd line] !=-1} {
if {[regexp "\===*" $line]} {
while {[gets $fd line] >= 0} {
append data "$line "
if {[regexp "\====*" $line]} {
break
} }
set topname [lindex $data 0]
regexp {(^[a-z]*) (.*) (.*module)} $data match topname instance sub3
puts "top name :$topname "
puts "instance: $instance"
}
close $fd
I am able to output topname and instance name only, not other values
Also please help me extract these values.
With this sort of task, it really helps if you put parts of the task into procedures that do just a simpler bit. For example, suppose we were to split the processing of a single section into its own procedure. Since it is only going to do one section (presumably a lot shorter than the overall file), it can work on a string (or list of strings) instead of having to process by lines, which will make things greatly easier to comprehend.
For example, it would handle just this input text:
apn
u_apn_ttp_logic
abc
u_apn_wgt_op_rd_u_apn_sca_u_part_sel1_sl_69_33
module:shift_right_vlog_unsigned_4662_7709
very_fast/barrel >> x 25x5 25 223.02 69 33 part_select.v
We might handle that like this:
proc processSection {sectionText} {
set top ""
set instance ""
set module ""
set other {}
foreach line [split $sectionText "\n"] {
if {$top eq ""} {
set top [string trim $line]
continue
}
if {$module eq ""} {
# This regular expression matches lines starting with “module:” and
# extracts the rest of the line
if {[regexp {^module:(.*)} $line -> tail]} {
set module [string trim $tail]
} else {
append instance [string trim $line] "/"
}
continue
}
# This regular expression matches a sequence of non-space characters, and
# the -all -inline options make regexp return a list of all such matches.
lappend other {*}[regexp -all -inline {\S+} $line]
}
# Remember to remove trailing “/” character of the instance
set instance [string trimright $instance /]
# Note that this splits apart the list in $other
outputSectionInfo $top $instance $module {*}$other
}
We also need something to produce the output. I've split it into its own procedure as it is often nice to keep parsing separate from output.
proc outputSectionInfo {top instance module arch op sgn in out area col row file} {
# Output the variables
foreach {label varname} {
"Top name" top
"Instance" instance
"Module" module
"Architecture" arch
"Operator" op
"Sign" sgn
"Input Size" in
"Output" out
"Area" area
"Column" col
"Row" row
"File Name" file
} {
# Normally, best to avoid using variables whose names are in variables, but
# for printing out like this, this is actually really convenient.
puts "$label = [set $varname]"
}
}
Now that we've got a section handler and output generator (and you can verify for yourself that these do sensible things, as they're quite a bit simpler than what you were trying to do in one go), we just need to feed the sections from the file into it, skipping over the header. The code does that, and just does that.
set fd [open "path_data.v"]
set flag 0
while {[gets $fd line] >= 0} {
if {[regexp {^=====+$} $line]} {
if {$flag} {
processSection [string trimright $accumulator "\n"]
}
set flag 1
set accumulator ""
} else {
append accumulator $line "\n"
}
}
close $fd
Your immediate problem was that your code was closing the channel too early, but that was in turn caused by your confusion over indentation, and that was in turn caused by you trying to do too much in one place. Splitting things up to make the code more comprehensible is the fix for this sort of issue, as it makes it much easier to tell that the code is definitely correct (or definitely wrong).
I worked on above script, and here is my code for the same. This code won't work if there is an empty line after "========" line
But I would like to explore your code as it is well organised.
set fd "[open "path_data.v" r]"
set fd1 "[open ./data_path_rpt.xls w+]"
puts $fd1 "Top Name\tInstance\tModule\tArchitecture\tOperator\tSign\tInput Size\tOutput size\tArea\tLine number\tColumn number\tFile Name"
set data {}
while {[gets $fd line] !=-1} {
if {[regexp "\===*" $line]} {
set data {}; set flag 0
while {[gets $fd line] >= 0} { append data "$line "
if {[regexp {[a-z]\.v} $line]} { set flag 1;break} }
puts "$data\n\n"
if {$flag} {
set topname [lindex $data 0]
regexp {(^[a-z]*) (.*) (.*module\:)(.*)} $data match topname instance sub3 sub4
set inst_name {} ;
foreach txt $instance {
append inst_name "$txt\/"
}
set instance [string trim $inst_name "\/"]
set module [lindex $sub4 0]
set architecture [lindex $sub4 1]
set operator [lindex $sub4 2]
set sign [lindex $sub4 3]
set input_size [lindex $sub4 4]
set output_size [lindex $sub4 5]
set area [lindex $sub4 6]
set linenum [lindex $sub4 7]
set col_num [lindex $sub4 8]
set file_name [lindex $sub4 9]
puts $fd1 "$topname\t$instance\t$module\t$architecture\t$operator\t$sign\t$input_size\t$output_size\t$area\t$linenum\t$col_num\t$file_name"
set data {} ; set flag 0
}}
}
close $fd1
close $fd
I have a list variable containing some values:
lappend list {query1}
{query2}
{query3}
And some data in file1 with parts of them matching the values above
query1 first data
query1 different data
query1 different data
query2 another data
query2 random data
query3 data something
query3 last data
How do I create a regexp loop that catches only the first instance found of each query and prints them out? In this case the output would be:
query1 first data
query2 another data
query3 data something
Attempted code to produce the output
set readFile1 [open file1.txt r]
while { [gets $readFile1 data] > -1 } {
for { set n 0 } { $n < [llength $list] } { incr n } {
if { [regexp "[lindex $list $n]" $data] } {
puts $data
}
}
}
close $readFile1
I tried using a for loop while reading the data from a file, but it seems to catch all values even if the -all option is not used.
You can either read the file as a whole into a variable using read command, if the text file is smaller in size. Apply the regexp for the content and we can extract the required data.
set list {query1 query2 query3}
set fp [open file1.txt r]
set data [read $fp]
close $fp
foreach elem $list {
# '-line' flag will enable the line sensitive matching
if {[regexp -line "$elem.+" $data line]} {
puts $line
}
}
If suppose the file too large to hold or if you consider run-time memory usage, then go ahead with the reading the content line by line. There we need to have control on what already matched for which you can keep an array to maintain whether the first occurrence of any query matched or not.
set list {query1 query2 query3}
set fp [open file1.txt r]
array set first_occurence {}
while {[gets $fp line]!=-1} {
foreach elem $list {
if {[info exists first_occurence($elem)]} {
continue
}
if {[regexp $elem $line]} {
set first_occurence($elem) 1
puts $line
}
}
}
close $fp
Reference : regexp
package require fileutil
set queries {query1 query2 query3}
set result {}
::fileutil::foreachLine line file1.txt {
foreach query $queries {
if {![dict exists $result $query]} {
if {[regexp $query $line]} {
dict set result $query $line
puts $line
}
}
}
}
The trick here is to store the findings in a dictionary. If there is a value corresponding to the query in the dictionary already, we don’t search for it again. This also has the advantage that the found lines are available to the script after the search and aren’t just printed out. The regexp search looks for the query string anywhere in the line: if it should only be in the beginning of the line, use regexp ^$query $line instead.
Documentation: dict, fileutil package, foreach, if, package, puts, regexp, set
Try This,
set fd [open "query_file.txt" r]
set data [read $fd]
set uniq_list ""
foreach l [split $data "\n"] {
lappend uniq_list [lindex $l 0]
}
set uniq_list [lsort -unique $uniq_list]
foreach l $uniq_list {
if {[string equal $l ""]} {
continue
}
foreach line [split $data "\n"] {
if {[regexp $l $line]} {
puts "$line"
break
}
}
}
close $fd
References: file , list , regexp
Not using regexp at all: I assume your "query"s do not contain whitespace
set list [list query1 query2 query3]
array set seen {}
set fh [open file1]
while {[gets $fh line] != -1} {
set query [lindex [split $line] 0]
if {$query in $list && $query ni [array names seen]} {
set seen($query) 1
puts $line
}
}
query1 first data
query2 another data
query3 data something
Ok so tcl expert here (Brad Lanam) wrote the following regexp and regsub commands in a tcl script to parse my file format (called liberty (.lib) used in chip design). I just want to know what they mean (if not why they were used since you don't have the context). I have used the references on tcl wiki but simply cannot seem to connect the dots. Here's the snippet of his code
set fh [open z.lib r]
set inval false
while { [gets $fh line] >= 0 } {
if { [regexp {\);} $line] } {
set inval false
}
if { [regexp {index_(\d+)} $line all idx] } {
regsub {^[^"]*"} $line {} d
regsub {".*} $d {} d
regsub -all {,} $d {} d
dict set risedata constraints indexes $idx $d
}
if { $inval } {
regsub {^[^"]*"} $line {} d
regsub {".*} $d {} d
regsub -all {[ ,]+} $d { } d
set row [expr {$rcount % 5}]
set column [expr {$rcount / 5}]
set i 0
foreach {v} [split $d { }] {
set c [lindex [dict get $risedata constraints indexes 3] $i]
dict set risedata constraints constraint $c $row $column $v
incr i
}
incr rcount
}
if { [regexp {values} $line] } {
set inval true
set row 0
set rcount 0
}
}
close $fh
Especially, what does
if { [regexp {index_(\d+)} $line all idx] } {
regsub {^[^"]*"} $line {} d
regsub {".*} $d {} d
regsub -all {,} $d {} d
Mean?? does line containing \d+ search for line variable for more than one digit and match against the string line ? What is regsub {^[^"]*"} $line {} d ?
Big thanks for helping a noob like me understand.
Reference: Brad Lanam
I'll take it line-by-line and explain what it appears to be doing.
if { [regexp {index_(\d+)} $line all idx] } {
This first line checks to see if the string stored in line includes
a substring of index_ followed by 1 or more digits. If so, it
stores the matching substring in all (which the rest of the code
appears to ignore) and stores the digits found in the variable idx.
So if line were set to "stuff index_123 more stuff", you would end
up with all set to index_123 and idx set to 123.
regsub {^[^"]*"} $line {} d
This regsub will remove everything from the beginning of line up
to and including the first double-quote. It stores the result in d.
regsub {".*} $d {} d
The next regsub operates on the value now in d. It looks for a
double-quote and removes that character and everything afterward,
storing the result again in d.
regsub -all {,} $d {} d
Finally, this line deletes any commas found in d, storing the result
back in d.
The next set of regexp/regsub lines perform a similar set of
operations except for the last line in the group:
regsub -all {[ ,]+} $d { } d
After the previous lines removed everything except the section that
had been in double-quotes, this line removes any sections made up of
one or more spaces and commas and substitutes them with a single
space.
Let me know if that is clear.
I have a string value in tcl as
set out " ABC CDE EFG
123 456"
I want to get the text that is present below text "EFG".
As right now it is "456", but it can be anything so I need a way though which I can grep for "EFG" and get the text below it.
This answer takes some inspiration from Johannes Kuhn's answer, but I use regexp to get the word indices from the "keys" line.
# this is as close as I can get to a here-doc in Tcl
set out [string trim {
ABC DEF GHI
123 456
}]
# map the words in the first line to the values in the 2nd line
lassign [split $out \n] keys values
foreach range [regexp -all -inline -indices {\S+} $keys] {
set data([string range $keys {*}$range]) [string range $values {*}$range]
}
parray data
outputs
data(ABC) = 123
data(DEF) =
data(GHI) = 456
I Suggest splitting the string into the keys and values with
lassign [split $out \n] keys values
and then look for the string position in the keys and get the same range in the values
set start [string first "EFG" $keys]
set value [string range $values $start [expr {${start}+[string length "EFG"]-1}]]
wraping it in a proc and we get
proc getValue {input lookFor} {
lassign [split $input \n] keys values
set start [string first $lookfor $keys]
set value [string range $values $start \
[expr {${start}+[string length $lookfor]-1}]]
}
invoke it like that:
getValue $out "EFG"
Edit: how is the 2nd line aligned? With a tabulator (\t), spaces?
In this case what you actually have is two lines with groups of 3 alphanumeric characters separated by spaces with a large amount of leading whitespace prefixing the second line ("\x20ABC\x20CDE\x20EFG\n[string repeat \x20 10]123[string repeat \x20 5]456" will reproduce what you posted). In your example [string range end-2 end] would give you what you need. I'd suggest reading the file line by line and each time you see the EFG, on the next line extract the part you need (maybe using string range) and emit it.
For example (untested):
set state 0
set f [open $inputfile r]
while {[gets $f line] != -1} {
if {$state} {
puts [string range $line end-2 end]
set state 0
} else {
if {[string match "*EFG" $line]} { set state 1 }
}
}
close $f