I am using tcl. Below is the code I have so far; is there a better way to do this?
if {$parname == "Wbay1" || $parname == "Wbay2" } {
set count [string index $parname end]
set Wbay$count [lindex $elem 1]
puts "set Wbay$count [lindex $elem 1]"
}
Be more general like this
if {$parname == "Wbay*" } {
set count [string index $parname end]
set Wbay$count [lindex $elem 1]
puts "set Wbay$count [lindex $elem 1]"
}
If the names are Wbay1, ..., Wbay9, you can use
if {[string match {Wbay[1-9]} $parname]} {
set $parname [lindex $elem 1]
}
If the number part can be greater than 9, you should use
if {[regexp {Wbay\d+} $parname]} {
set $parname [lindex $elem 1]
}
A regexp (regular expression) match is more powerful than a string match. In this case, \d+ means "one or more digits".
If you want to record the highest number you've seen so far, use
set maxN 0
...
if {[regexp {Wbay(\d+)} $parname -> n]} {
set maxN [expr {max($n, $maxN)}]
set $parname [lindex $elem 1]
}
The parenthesis means that you want to capture the matched string within, i.e. the number. The -> symbol is a variable name: it's a convention that is often used to store the full match (e.g. "Wbay7") when we don't care about it. The variable n is set to the number that was captured. If regexp doesn't return 1, the value in n can't be trusted: the variable will keep whatever value it had before. The variable maxN is set to whichever of $n and $maxN is greatest.
But you might also find an array variable useful. With an array, you name the individual members Wbay(1), Wbay(2), Wbay(99), etc (they don't have to be consecutive or in order). If you want to know how many members you have, array size Wbay will tell you.
Documentation:
array,
expr,
if,
lindex,
max (function),
regexp,
set,
string,
Syntax of Tcl regular expressions
Syntax of Tcl string matching:
* matches a sequence of zero or more characters
? matches a single character
[chars] matches a single character in the set given by chars (^ does not negate; a range can be given as a-z)
\x matches the character x, even if that character is special (one of *?[]\)
Related
I have a list and need to search some strings in this list. My list is like following:
list1 = {slt0_reg_11.CK slt0_reg_11.Q slt0_reg_12.CK slt0_reg_12.Q}
I am trying to use lsearch to check if above list includes some strings or not. Strings are like:
string1 = {slt0_reg_1 slt0_reg_1}
I am doing the following to check this:
set listInd [lsearch -all -exact -nocase -regexp $list1 $string1]
This commands gives the indexes if list1 includes $string1 (This is what I want). However, problem is if I have a string like slt0_reg_1, the above command identifies the first two elements of the list (slt0_reg_11.CK slt0_reg_11.Q) because these covers the string I search.
How can I make exact search?
It sound like you want to add in word-boundary constraints (\y) to your RE. (Don't use -exact and -regexp at the same time; only one of those modes can be used on any run because they change the comparison engine used.) A little care must be taken because we can't enclose the RE in braces as we want to do variable substitution within it.
set list1 {slt0_reg_11.CK slt0_reg_11.Q slt0_reg_12.CK slt0_reg_12.Q}
foreach str {slt0_reg_11 slt0_reg_1} {
set matches [lsearch -all -regexp $list1 "\\y$str\\y"]
puts "$str: $matches"
}
Prints:
slt0_reg_11: 0 1
slt0_reg_1:
If you want to compare your list for an exact match of the part before the dot against another list, you may be better off using lmap:
set index -1
set listInd [lmap str $list1 {
incr index
if {[lindex [split $str .] 0] ni $string1} continue
set index
}]
I have a question regarding possibility of getting string after specific character in TCL.
Whan I mean is :
Input:
abcdefgh = hgfedcba
Output:
hgfedcba
(return everything after "=" without possible whitespaces)
This is what I was using:
regexp {abcdefgh=\s+"(.*)"} $text_var all variable
In some cases it is ok (with spaces) but when there is no whitespaces then it is not working.
Assuming
% set s {abcdefgh = hgfedcba}
# => abcdefgh = hgfedcba
(or the same thing without one or both of the blanks) you could do one of these:
% scan $s {%*[^=]= %s}
# => hgfedcba
(Scan the string for a substring not containing "=", then advance past the equals sign and optional whitespace, then return the rest of the string.)
string trim [lindex [split $s =] 1]
(Split the string at the equals sign, return the (whitespace-trimmed) second resulting element.)
string trim [string range $s [string first = $s]+1 end]
(Return the (whitespace-trimmed) substring starting after the equals sign.)
string trim [lindex [regexp -inline {[^=]+$} $s] 0]
(Return the (whitespace-trimmed) first match of one or more characters, not including the equals sign, anchored on the end of the string.)
lindex [regexp -inline -all {[a-h]+} $s] 1
(Return the second match of consecutive characters from the set "a" to "h".)
string trimleft [string trimleft $s {abcdefgh }] {= }
(Remove all characters from the start of the string that occur in the set "a" to "h" and blank, then remove from start of the resulting string any characters that are equals sign or blank.)
% regexp {abcdefgh\s*=\s*(\S+)} "abcdefgh = hgfedcba" all variable
1
% set variable
hgfedcba
% regexp {abcdefgh\s*=\s*(\S+)} "abcdefgh=hgfedcba" all variable
1
% set variable
hgfedcba
%
I have this string:
svpts-7-40.0001
And I need to remove the second '-' from this.
Basically I am fetching values like these which would come with double '-' SOMETIMES. So if such variables are seen then I have to remove the second '-' and replace the same with '.' , so the string should look like:
svpts-7.40.0001
[EDIT] I have tried:
% set list1 [split $string -]
svpts 7 40.0001
% set var2 [join $list1 .]
svpts.7.40.0001
%
Here's a regular expression that will change only the 2nd hyphen:
% regsub -expanded {( .*? - .*? ) -} "svpts-7-40.0001" {\1.}
svpts-7.40.0001
% regsub -expanded {( .*? - .*? ) -} "svpts-7_40.0001" {\1.}
svpts-7_40.0001
% regsub -expanded {( .*? - .*? ) -} "svpts-7-40.0001-a-b-c" {\1.}
svpts-7.40.0001-a-b-c
Try
% set data svpts-7-40.0001
svpts-7-40.0001
% regexp {([^-]*-)(.*)} $data -> a b
1
% set b [string map {- .} $b]
7.40.0001
% set newdata $a$b
svpts-7.40.0001
The above code changes every hyphen after the first. To change only the second hyphen, one can do this:
set idx [string first - $data [string first - $data]+1]
set newdata [string replace $data $idx $idx .]
or this:
set idxs [lindex [regexp -inline -all -indices -- - $data] 1]
set newdata [string replace $data {*}$idxs .]
The first snippet is well-behaved if the data string doesn't contain at least two hyphens; the other needs some kind of checking to avoid throwing an error.
Documentation:
lindex,
regexp,
set,
string,
{*} (syntax),
Syntax of Tcl regular expressions
Syntax of Tcl index expressions:
integer zero-based index number
end the last element
end-N the nth element before the last element
end+N the nth element after the last element (in practice, N should be negative)
M-N the nth element before element m
M+N the nth element after element m
There can be no whitespace within the expression.
how can I convert
set var "USE_90a_Sc_ttv"
to
set out "9.0a ttv Sc"
using tcl code?
Regards,
Divesh
Use the split, lassign and regsub functions:
lassign [split $var _] prefix version type tag
regsub {(\d(\w)?)$} $version {.\1} nversion
set out "$nversion $tag $type"
If you are using an older version and don't have lassign available, you
can use lindex to retrieve specific items from the list returned by split.
set tlist [split $var _]
set version [lindex $tlist 1]
set type [lindex $tlist 2]
set tag [lindex $tlist 3]
regsub {(\d(\w)?)$} $version {.\1} nversion
set out "$nversion $tag $type"
I'd use scan to parse that, and list to assemble the results.
set var "USE_90a_Sc_ttv"
# Remember to check the result of [scan] for number of parsed fields
if {[scan $var {USE_%1d%2[^_]_%2[^_]_%3s%c} a b c d e] != 4} {
error "Unexpected input data! '$var'"
}
set out [list $a.$b $d $c]
Putting a %c at the end of the format lets me detect if there are any unexpected characters at the end. There shouldn't be; only 4 fields ought to be satisfied. This makes for a quick way to check that what I've got is what I expect. Also, %2[^_] is an unusual field specifier, but all it does is ask for 2 non-underscore characters.
I have a string in this pattern:
2(some_substring) -> 3(some_other_substring)
Now these number can be anything.
I think this answer would solve the problem. But it gives all the integers in one variable. I want them to be in different variables, so that I can analyze them. Can we split it? But Splitting would cause problem:
If the the numbers are not single-digit, then the splitting will be erroneous.
Is there any other way?
You can use a variation of this: instead of removing the non-digit characters, you can extract all digit characters into a list:
set text {2(some_substring) -> 3(some_other_substring)}
set numbers [regexp -all -inline -- {[0-9]+} $text]
puts $numbers
# => 2 3
And to get each number, you can use lindex:
puts [lindex $numbers 0]
# => 2
Or in versions 8.5 and later, you can use lassign to assign them to specific variable names:
lassign $numbers first second
puts $first
# => 2
puts $second
# => 3
In regexp -all -inline -- {[0-9]+} $text, -all extract all the matches, -inline puts the matches into a list, -- ends the options, [0-9]+ matches at least one integer.
To extend Jerry's answer, in case digits can appear within the parentheses, a regular expression to only extract digits that are immediately followed by an open parenthesis is: {\d+(=\()}
% set text {2(some_6substring) -> 3(some_other_5substring)}
2(some_6substring) -> 3(some_other_5substring)
% lassign [regexp -all -inline {\d+(?=\()} $text] first second
% set first
2
% set second
3
This assumes that you don't have nested parentheses.