TCL Regular Expression Doubt - regex

As per my understanding of RE
--> * means matches 0 or more occurrences of prev regex
--> + means matches 1 or more occurrences of prev regex
Now lets take a look at the following examples
FIRST:-
% regexp {:+} "DHCP:Enabled" first
1
% puts $first
: --> ":" is stored in variable first
%
SECOND:-
% regexp {:*} "DHCP:Enabled" sec
1
% puts $sec
--> Nothing is stored in variable second
%
Why is ":" stored for the FIRST one and not the SECOND?

The second regexp {:*} matches the empty string because the empty string is 0 occurrences of :. If you use the -indices option for regexp, you'll see that it matches at position 0.
% regexp -indices :* "DHCP:Enabled" indices
1
% puts $indices
0 -1
In other words, the regexp matches at the first character and returns.

It matches the empty string so that it can
match that empty string at the start of “DHCP:Enabled”. The regular
expression engine like to match things up as soon as possible. To show, here's an interactive session:
% regexp -inline {:*} "DHCP:Enabled"
{}
% regexp -inline -all {:*} "DHCP:Enabled"
{} {} {} {} : {} {} {} {} {} {} {}
% regexp -inline -indices -all {:*} "DHCP:Enabled"
{0 -1} {1 0} {2 1} {3 2} {4 4} {5 4} {6 5} {7 6} {8 7} {9 8} {10 9} {11 10}
The -inline option is useful for simple testing, the -all matches in
every matchable location instead of just the first, and the -indices
returns locations rather than the string.
Note that only once (4 4) is the end at least at the same index as the start; in all other cases, an empty string matches (and it's legal; you said that matching nothing was OK).
In general, it's a really good idea to make sure that your overall RE cannot match the empty string or you'll be surprised by the results.

Related

How to use expr inside if statement?

I have not managed to understand how to embed expr in other constructs.
I can easily type
set i {1 2 3}
expr {[llength $i]} #result is 3
However after long research I have not managed to find a way to put that inside if
if {"magic tcl code with expr and llength==3 required here"} {
puts "length is 3"
}
The first argument to if is an expression, just as the argument to expr is an expression.
if {[llength $i] == 3} {
puts "length is 3"
}
You can indeed put expr inside an expression using [brackets], just as with any command, but there's usually no reason to do so; it just makes everything more verbose.
if {[ expr {[llength $i]} ] == 3} {
puts "length is 3"
}
The exception to the above rule comes when you've got an expression that's somehow dynamic; that's when putting an expr inside an expression makes sense as it allows the outer parts to be bytecode-compiled efficiently.
# Trivial example to demonstrate
set myexpression {[llength $i]}
if {[expr $myexpression] == 3} {
puts "evaluated $myexpression to get three"
}
That's pretty rare.
The if command has a built-in expr. Specifically both if and expr expects and expression as the first argument:
if expression ...
expr expression
Therefore, if you want to execute expr {[llength $i]} then just do:
if {[llength $i]} {
}
But to tell you the truth, I don't believe you want to execute
expr {[llength $i]}
I think what you want is to check if the length is 3, therefore what you really want is
expr {[llength $i] == 3}
If that is the case, then the if version would be:
if {[llength $i] == 3} {
puts "llength is 3"
}

How do I check in TCL if a character from a string is alphanumeric or not?

I need to check if characters from a string, taken individually, are alphanumeric, spaces or special characters. Unable to find a definitive way of doing this.
For example,
set str1 = "It is 5!"
so, my output should be:
I - Alphanumeric
t - Alphanumeric
" " - Space
i - Alphanumeric
s - Aplhanumeric
" " - Space
5 - Alphanumeric
! - Special Character
The string is command can be used to find out whether all the characters of a string are a member of a particular character class, can can be told to also report the index of the first character which is not a member of that class.
% string is alpha "abc"
1
% string is alpha -failindex ix "abc123"
0
% puts $ix
3
Relevant classes might be alpha, digit, space and punct. Also, alnum is the union of alpha and digit.
After a good answer of #Donal.. Still if you want to use regexp as you mentioned in TAG.. You can probably do like this,
set var "Abc Def 8 9"
for {set i 0} {$i < [string length $var]} {incr i} {
set char [string index $var $i]
if {[regexp -all {[a-zA-Z0-9]} $char]} {
puts "$char is Alphanumeric"
} else {
puts "$char is Special Character"
}
}
It will solved your purpose of getting each character of string one by one... and check whether it's alphanumeric or special character..

How to use foreach inside subst in Tcl (template iteration)?

Does anyone know how if there is a way to include a foreach loop in the subst command, to get a pseudo-template effect?
For example, the following works:
set lim 3
set table sldkfjsl
set sqlpat {
select * from $table limit $lim
}
set sqltext [subst $sqlpat]
But I would like to do something like
set sqlpat {
foreach i {1 2 3} {
select * from ${table}_$i limit $lim;
}
}
set sqltext [subst $sqlpat]
And have it give three separate lines of sql:
select * from sldkfjsl_1 limit 3
select * from sldkfjsl_2 limit 3
select * from sldkfjsl_3 limit 3
Any ideas? Thanks!
(EDIT, my solution which sort of shows how build a strfor command that can be used in a subst template, in my case for passing both SQL and gnuplot code to their respective programs):
proc strfor { nms vals str } {
set outstr ""
foreach $nms $vals {
append outstr [subst $str]
}
return $outstr
}
set foostr1 {select $a from table_$b;\n}
set x [strfor {a b} {A 1 B 2 C 3 D 4} $foostr1]
set foostr2 {
blahsd line 1
blahg line 2
[strfor {a b} {A 1 B 2 C 3 D 4} {
forline1 $a $b
forline2 $b $a
}]
blah later
}
puts [subst $foostr2]
The looping commands in Tcl do not return values, so they are useless in a string which is processed with subst. It is of course possible to write an accumulating looping command as you have done. Another possibility is to use lmap. However, the problem can be solved in an easier way.
set lim 3
set table sldkfjsl
We're going to make a list where every item is an instance of a literal template with variable substitutions. First we create an empty list:
set sqlpats {}
Then we loop for each value in the sequence 1..3. For every iteration we append an instance of the template to the list:
foreach i {1 2 3} {
lappend sqlpats "select * from ${table}_$i limit $lim"
}
(subst isn't necessary here, ordinary variable substitution is sufficient.)
Create a resulting string from the list, with newlines between each item (yep, I was wrong, one more command was needed):
join $sqlpats \n
ETA:
subst is one of those commands which is nice to have, but that I for one almost never use. For most purposes, simpler measures will do. Once in a while though, a convoluted bit of code leaves a string unsubstituted. I pick up subst out of the drawer and zap! That said, the ability to selectively allow or disallow different kinds of substitutions alone makes subst very worthwhile.
Documentation: foreach, join, lappend, lmap, set, subst

How to get values inside nested braces using perl

I have a string having list of expressions inside braces. I want to get the details by splitting it in an array.
I have tried like this.
#!/usr/bin/perl
sub main() {
my $string = <STDIN>;
while ($string =~ /(\((?:(?1)|[^()]*+)++\))|[^()\s]++/g)
{
print "$&\n"
}
main();
InPut : (+ (+ 4 3) ( - 3 2) 5)
Output should be : (+ (+ 3 4) ( - 2 3) 5)
(+ 3 4)
( - 2 3)
which i'm trying to store it in an array and then evaluate seprately.. But not sure thats the right approach.
Basically i'm trying to evaluate an expression as below.
4+3 =7 , 3-2 =1 , and then 7+1+5 = 13
Final output should be 13
Can any one kindly help me on this?
Use the following expression /(?=(\((?>[^()]+|(?1))*\)))/g
See it in action here: http://regex101.com/r/eI7iP5

regex for position matching with OR condition

Newbie to regex and looking for help in creating regexp to seek out following:
The data items consists of six character strings as shown in example below
1) "100100"
2) "110011"
3) "010000"
4) "110011"
5) "111111"
6) "000111"
Need to use regexp to find data with say
1 in the 1st position OR 1 in the 4th position: Items 1, 2, 4, 5 and 6 should be matched
1 in 2nd position: Items 2,4 ad 5 should be matched
1 in 5th and 6th position: Items 2, 4, 5 and 6 should be matched
Given your samples, these will work:
1 in the 1st position OR 1 in the 4th position: Items 1, 2, 4, 5 and 6 should be matched
1.....|...1...
1 in 2nd position: Items 2,4 ad 5 should be matched
.1....
1 in 5th and 6th position: Items 2, 4, 5 and 6 should be matched
....11
Or if you want to match any of these rules, combine them with the | (or) operator.
Example:
http://regexpal.com/?flags=g&regex=(1.....%7C...1...%7C.1....%7C....11)&input=100100%0A%0A110011%0A%0A010000%0A%0A110011%0A%0A111111%0A%0A000111
If it is always strings with only 1s and 0s, you should treat them as binary numbers and use logical operators to find the matches.
Try this regex
([1][0-1]{2}[1][0-1]{2})|([0-1][1][0-1]{4})|([0-1]{4}[1]{2})
Find the explanation and demo here http://www.regex101.com/r/vD9jE7
Here's an example. Change dots with zeros if necessary. /^(11..|.1.1)11$/
^ # beginning of string
( # either
11.. # 11 and any 2 char
| # or
.1.1 # any char, 1, any char, 1
)
11
$ # end of string