How to replace _NUM with _* using regsub - tcl

I want to convert both strings into SML_CHAINS_6_* using regsub in tcl. How do I do that?
SML_CHAINS_6_1167
SML_CHAINS_6_1145

Something like:
set var1 SML_CHAINS_6_1167
regsub {\d+$} $var1 "*" var1
and same for the other. Just replacing all trailing digits with a single asterisk.

Related

Split a string into words which are enclosed in single quotes

I want to split a string into separate words which which are enclosed in single quotes like below:
For example:
set str {'Name' 'Karna Mayer' ''}
I want to split this into 3 separate words. How can this be performed using Tcl.
For this sort of task, I'd use regexp -all -inline and lmap (to drop the unwanted bits from the results of that).
set input "'Name' 'Karna Mayer' ''"
set output [lmap {- bit} [regexp -all -inline {'([^'']*)'} $input] {set bit}]
The good thing about this is that if you have a way of escaping a single quote in that, you can use a more complex regular expression and match that too.
set output [lmap {- bit} [regexp -all -inline {'((?:\\.|[^''])*)'} $input] {
string map {\\ {}} $bit
}]
You can use string map to convert the single quotes to double quotes and escape existing quotes
set str [string map {{"} {\"} ' {"}} $str]
# "name" "Karna Mayer" ""
you can then use list and argument expansion to convert it to a list
set l [list {*}$str]
# Name {Karna Mayer} {}
full program
set str {'Name' 'Karna Mayer' ''}
set str [string map {{"} {\"} ' {"}} $str]
set l [list {*}$str]
If you use single quote as a separator, then you'll take every second element:
% set input "'Name' 'Karna Mayer' ''"
'Name' 'Karna Mayer' ''
% split $input {'}
{} Name { } {Karna Mayer} { } {} {}
We see: the empty string before the first quote; the first field; the space between the 1st and 2nd; the 2nd field; the next space; the (empty) 3rd field; and then the empty string after the last quote. We want to ignore this last element.
% set fields [lmap {_ field} [lrange [split $input {'}] 0 end-1] {set field}]
Name {Karna Mayer} {}
No thanks to the Tcl syntax highlighter.

How to set \n to a str in tcl

Hi I am new to tcl and want to assign "\n" to a variable and use that variable in regsub to replace that string.
It should be like :
set a "\n\[\"this is my code\"\]"
puts $a
I was expecting this will give
\n\[\"this is my code\"\], then I could use this $a in regsub {$a} $str "replace" sub_str.
This regsub could search $a inside $str and replace the matching one with replace and strore it in sub_str.
However, it gave me [this is my code]
Is there a way I could get the 1st format as \n\[\"this is my code\"\] so I could use that to do the string regsub?
Thanks!
Use braces instead of quotes to prevent evaluation of the backslash escapes:
set a {\n\[\"this is my code\"\]}
puts $a
prints
\n\[\"this is my code\"\]

How to use array variable in regexp expression (TCL)

In the file I have something like this:
name(0) = 123
name(1) = 456
name(2) = 789
I want to write match string to array.
for { set i 0 } { $i < 3 } { incr i } {
regexp {name\($i\) =\s+(.*)} $line full($i) name($i)
}
I don't know why regexp don't recognizes $i counter.
If I write:
regexp {name\(0\) =\s+(.*)} $line full($i) name($i)
working but only for first counter.
Braces in Tcl quote the string literally, so no variable substitution is done. If you want variable substitution, use double quotes. Since you are quoting a regular expression, the backslashes will need to be escaped.
Convert:
{name\($i\) =\s+(.*)}
To:
"name\\($i\\) =\\s+(.*)"
Or as DKF has suggested. This makes it easier to see the regexp without all the backslashes
set pattern [format {name\(%d\) =\s+(.*)} $i]
regexp $pattern $line full($i) name($i)
References: Tcl syntax, regex syntax, format

how to find and replace sencond occurance of string using regsub

I am new to tcl, trying to learn, need a help for below.
My string looks like in configFileBuf and trying to replace second occurance of ConfENB:local-udp-port>31001" with XYZ, but below regsub cmd i was tried is always replacing with first occurance (37896). Plz help how to replace second occurance with xyz.
set ConfigFileBuf "<ConfENB:virtual-phy>
</ConfENB:local-ip-addr>
<ConfENB:local-udp-port>37896</ConfENB:local-udp-port>
</ConfENB:local-ip-addr>
<ConfENB:local-udp-port>31001</ConfENB:local-udp-port>
</ConfENB:virtual-phy>"
regsub -start 1 "</ConfENB:local-ip-addr>\[ \n\t\]+<ConfENB:local-udp-port>\[0-9 \]+</ConfENB:local-udp-port>" $ConfigFileBuf "XYZ" ConfigFileBuf
puts $ConfigFileBuf
You have to use regexp -indices to find where to start the replacement, and only then regsub. It's not too bad if you put the regular expression in its own variable.
set RE "</ConfENB:local-ip-addr>\[ \n\t\]+<ConfENB:local-udp-port>\[0-9 \]+</ConfENB:local-udp-port>"
set start [lindex [regexp -all -indices -inline $RE $ConfigFileBuf] 1 0]
regsub -start $start RE $ConfigFileBuf "XYZ" ConfigFileBuf
The 1 is the number of submatches in the RE (zero in this case) plus 1. You can compute it with the help of regexp -about, giving this piece of trickiness:
set RE "</ConfENB:local-ip-addr>\[ \n\t\]+<ConfENB:local-udp-port>\[0-9 \]+</ConfENB:local-udp-port>"
set relen [expr {1 + [lindex [regexp -about $RE] 0]}]
set start [lindex [regexp -all -indices -inline $RE $ConfigFileBuf] $relen 0]
regsub -start $start RE $ConfigFileBuf "XYZ" ConfigFileBuf
If your string was well-formed XML I'd suggest something like tDOM to manipulate it. DOM-style manipulation is almost always better than regular expression-based manipulation on XML markup. (I mention this on the off chance that it's actually supposed to be XML and you just quoted it wrong.)
It looks like you're trying to use -start 1 to tell regsub to skip the first match. The starting index is actually a character index, so in this invocation regsub will just skip the first character in the string. You could set -start further into your string, but that's fragile unless you use regexp to calculate where the first match ends.
I think the best solution would be to get a list of indices to matches by invoking regexp with -all -inline -indices, pick out the second index pair using lindex and finally use string replace to perform the substitution, like this:
set pattern {</ConfENB:local-ip-addr>[ \n\t]+<ConfENB:local-udp-port>[0-9 ]+</ConfENB:local-udp-port>}
set matches [regexp -all -inline -indices -- $pattern $ConfigFileBuf]
set match [lindex $matches 1]
set ConfigFileBuf [string replace $ConfigFileBuf {*}$match XYZ]
The variable match contains a pair of indices (start and end, respectively) for the range of characters you want to replace. As string replace expects those indices to be in different arguments you need to expand $match with the {*} prefix. If you have an earlier version of Tcl than 8.5, you need a slight change to the above code:
foreach {start end} $match break
set ConfigFileBuf [string replace $ConfigFileBuf $start $end XYZ]
In passing, note that you can avoid escaping e.g. character sets in a regular expression if you quote it with braces instead of double quotes.
Documentation links: regexp, lindex, string

TCL command - string trim

I was using the command 'string trimright' to trim my string but I found that this command trims more than required.
My expression is "dssss.dcsss" If I use string trim command to trim the last few characters ".dcsss", it trims the entire string. How can I deal with this?
Command:
set a [string trimright "dcssss.dcsss" ".dcsss"]
puts $a
Intended output:
dcsss
Actual output
""
The string trimright command treats its (optional) last argument as a set of characters to remove (and so .dcsss is the same as sdc. to it), just like string trim and string trimleft do; indeed, string trim is just like using both string trimright and string trimleft in succession. This makes it unsuitable for what you are trying to do; to remove a suffix if it is present, you can use several techniques:
# It looks like we're stripping a filename extension...
puts [file rootname "dcssss.dcsss"]
# Can use a regular expression if we're careful...
puts [regsub {\.dcsss$} "dcssss.dcsss" {}]
# Do everything by hand...
set str "dcssss.dcsss"
if {[string match "*.dcsss" $str]} {
set str [string range $str 0 end-6]
}
puts $str
If what you're doing really is filename manipulation, like it looks like, do use the first of these options. The file command has some really useful commands for working with filenames in a cross-platform manner in it.