This question is about understanding the behaviour of a specific regex in TCL 8.5 built into Vivado, in particular or
-ing together two regex parts I get unexpected results:
I worked on indenting a block of text for the command line using regular expressions. My first thought was to replace every newline
by a newline
and some spaces
(replaced by X
here for clarity) for indentation, so:
puts [regsub -all "\n" "foo\nBar\nBaz" "\nXX"]
foo
XXBar
XXBaz
This does not indent the first line, to match the first line I use ^
:
puts [regsub -all "^" "foo\nBar\nBaz" "\nXX"]
XXfoo
Bar
Baz
Now it should just be a matter of comibining the two regex parts with an |
, however I get output I can not explain:
puts [regsub -all "^|\n" "foo\nBar\nBaz" "\nXX"]
XXfoo
XX
XXBar
XX
XXBaz
demo
Where do the additonal newlines and identiation marks (X
) come from? Why does it look like I get two substitutions? Is this a bug, or is there a bit I do not understand about regular expression syntax?
For completnes sake here is the regex I use now puts [regsub -all -line "^" "foo\nBar\nBaz" "XX"]
Finally, flavors like std::regex and Tcl do not support lookbehind at all, even though they do support lookahead. JavaScript was like that for the longest time since its inception. But now lookbehind is part of the ECMAScript 2018 specification.
The "regexp" command is used to match a regular expression in Tcl. A regular expression is a sequence of characters that contains a search pattern. It consists of multiple rules and the following table explains these rules and corresponding use. Sr.No.
Special Regex Characters: These characters have special meaning in regex (to be discussed below): . , + , * , ? , ^ , $ , ( , ) , [ , ] , { , } , | , \ . Escape Sequences (\char): To match a character having special meaning in regex, you need to use a escape sequence prefix with a backslash ( \ ). E.g., \. matches "."
I think the explanation hinges on the fact that the expression ^
is treated as a basic regular expression (BRE), but when you add |
it is treated like an advanced regular expression (ARE), which is a superset of extended regular expressions (ERE). This is based on the following, from the re_syntax man page:
An ARE is one or more branches, separated by “|”, matching anything that matches any of the branches.
The second part of the puzzle is that ^
is treated differently in basic and extended/advanced regular expressions. In a basic regular expression, ^
only has a special meaning when it is the first character of the expression. Again, from the re_syntax man page:
BREs differ from EREs in several respects ... ^ is an ordinary character except at the beginning of the RE or the beginning of a parenthesized subexpression,...
In other words, for a BRE, ^
will only match the very start of the string, but in an ARE it will match the beginning of a line.
So, what exactly is happening?
First, ^
matches the beginning of a string, so it replaces it with the replacement \nXX
. Next, it sees f
, then o
, then o
, none of which matches. Then it sees '\n`, which it matches, so it replaces it with the replacement.
At this point the matcher has consumed the characters foo\n
. What remains is Bar\nBaz
. The matcher now looks at that string, and the pattern ^
matches, so it again replaces it with the replacement. Thus, you end up with two copies of the replacement string, one for the newline and one for the beginning of the string that remains.
If your end goal is to add indentation to every line, you can use newline sensitive matching with regsub and then use ^
to match every line including the first, rather than try to match both newlines and the start of the string. You do this by adding the --line
option to regsub
. For example:
regsub -line -all "^" "foo\nBar\nBaz" "XX" t; puts $t
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With