Is it possible to have do different substitutions in an expression using regsub?
example:
set a ".a/b.c..d/e/f//g"
Now, in this expression, is it possible to substitute
".
" as "yes
"
"..
" as "no
"
"/
" as "true
"
"//
" as "false
" in a single regsub command?
With a regsub
, no. There's a long-standing feature request for this sort of thing (which requires substitution with the result of evaluating a command on the match information) but it's not been acted on to date.
But you can use string map
to do what you want in this case:
set a ".a/b.c..d/e/f//g"
set b [string map {".." "no" "." "yes" "//" "false" "/" "true"} $a]
puts "changed $a to $b"
# changed .a/b.c..d/e/f//g to yesatruebyescnodtrueetrueffalseg
Note that when building the map, if any from-value is a prefix of another, the longer from-value should be put first. (This is because the string map
implementation checks which change to make in the order you list them in…)
It's possible to use regsub
and subst
to do multiple-target replacements in a two-step process, but I don't advise it for anything other than very complex cases! A nice string map
is far easier to work with.
EDIT: In Tcl 9.0, you can do this with regsub -command
:
regsub -all -command {\.\.?|//?} $a {
dict get {. yes .. no / true // false}
}
The inner selection of what to replace with is via dict get
as the neatest way to do this, but in general you're more likely to use apply
and a lambda expression like this:
regsub -all -command {\.\.?|//?} $a {apply {{substring} {
const MAPPING {. yes .. no / true // false}
return [dict get $MAPPING $substring]
}}}
Using a lambda lets you do much more complex transformations.
The argument passed to the inner command call is the substring that is matched (plus any capturing sub-REs as extra arguments), and the result of the inner command call is the substitution to use for the match.