For example I have a statement:
$var = '2*2-3+8'; //variable type is string
How to make it to be equal 9 ?
From this page, a very awesome (simple) calculation validation regular expression, written by Richard van Velzen. Once you have that, and it matches, you can rest assured that you can use eval over the string. Always make sure the input is validated before using eval!
<?php
$regex = '{
\A # the absolute beginning of the string
\h* # optional horizontal whitespace
( # start of group 1 (this is called recursively)
(?:
\( # literal (
\h*
[-+]? # optionally prefixed by + or -
\h*
# A number
(?: \d* \. \d+ | \d+ \. \d* | \d+) (?: [eE] [+-]? \d+ )?
(?:
\h*
[-+*/] # an operator
\h*
(?1) # recursive call to the first pattern.
)?
\h*
\) # closing )
| # or: just one number
\h*
[-+]?
\h*
(?: \d* \. \d+ | \d+ \. \d* | \d+) (?: [eE] [+-]? \d+ )?
)
# and the rest, of course.
(?:
\h*
[-+*/]
\h*
(?1)
)?
)
\h*
\z # the absolute ending of the string.
}x';
$var = '2*2-3+8';
if( 0 !== preg_match( $regex, $var ) ) {
$answer = eval( 'return ' . $var . ';' );
echo $answer;
}
else {
echo "Invalid calculation.";
}
What you have to do is find or write a parser function that can properly read equations and actually calculate the outcome. In a lot of languages this can be implemented by use of a Stack, you should have to look at things like postfix and infix parsers and the like.
Hope this helps.
$string_with_expression = '2+2';
eval('$eval_result = ' . $string_with_expression)`;
$eval_result - is what you need.
There is intval function
But you can't apply direct to $var
For parser Check this Answer
Related
I need replace "," with |,| inside pattern without replace any other place
i have like this code
[word:"pla pla","pla pla","[other_word:"pla pla","[word:"pla","pla"end word]","pla pla"end other_word]","pla pla","[word:"pla","pla"end word]"end word]
result must be like this
[word:"pla pla|,|pla pla|,|[other_word:"pla pla","[word:"pla","pla"end word]","pla pla"end other_word]|,|pla pla|,|[word:"pla","pla"end word]"end word]
my curent code is :
preg_replace('/\[([\w]+):\"[^\",\"]*\"end\s\w\](.*?)\[([\w]+):\"\"end\s\w\]/', '|^|', $syn);
This pattern is designed to replace "," only inside the first level or square brackets:
$pattern = '~
# this part defines subpatterns to be used later in the main pattern
(?(DEFINE)
(?<nestedBrackets> \[ [^][]* (?:\g<nestedBrackets>[^][]*)*+ ] )
)
# the main pattern
(?: # two possible entry points
\G(?!\A) # 1. contiguous to a previous match
| # OR
[^[]* \[ # 2. all characters until an opening bracket
)
# all possible characters until "," or the closing bracket:
[^]["]* # all that is not ] [ or "
(?:
\g<nestedBrackets> [^]["]* # possible nested brackets
| # OR
"(?!,") [^]["]* # a quote not followed by ,"
)*+ # repeat as needed
\K # remove all on the left from match result
(?:
"," # match the target
|
] (*SKIP)(*F) # closing bracket: break the contiguity
)
~x';
$str = preg_replace($pattern, '|,|', $str);
demo
I split a string '3(1-5)' like this:
$pattern = '/^(\d+)\((\d+)\-(\d+)\)$/';
preg_match($pattern, $string, $matches);
But I need to do the same thing for decimals, i.e. '3.5(1.5-4.5)'.
And what do I have to do, if the user writes '3,5(1,5-4,5)'?
Output of '3.5(1.5-4.5)' should be:
$matches[1] = 3.5
$matches[2] = 1.5
$matches[3] = 4.5
You can use the following regular expression.
$pattern = '/^(\d+(?:[.,]\d+)?)\(((?1))-((?1))\)$/';
The first capturing group ( ... ) matches the following pattern:
( # group and capture to \1:
\d+ # digits (0-9) (1 or more times)
(?: # group, but do not capture (optional):
[.,] # any character of: '.', ','
\d+ # digits (0-9) (1 or more times)
)? # end of grouping
) # end of \1
Afterwords we look for an opening parenthesis and then recurse (match/capture) the 1st subpattern followed by a hyphen (-) and then recurse (match/capture) the 1st subpattern again followed by a closing parenthesis.
Code Demo
This pattern should help:
^(\d+\.?\,?\d+)\((\d+\,?\.?\d+)\-(\d+\.?\,?\d+)\)$
I'm trying to create a regex to parse figures references inside a text. I must match at least these cases:
Fig* 1, 2 and 3 (not only 3, any number)
Fig* 1-3
Fig* 1 and 2
Fig* 1
Fig* 1 to 4
So I tried the following regex:
(Fig[a-zA-Z.]*)(\s(\d(,|\s)* )+|\d\s|and\s\d|\s\d-\d|\s\d)*
The best result would be having the numbers separated, but having the match I can just clean up the result and parse the numbers.
But I just can't seem to be able to parse that "1 to 4". Also, this regex seems not optmized at all. Any ideas?
Here is a sample: http://www.phpliveregex.com/p/3Zj
try this:
(Fig.*) ((\d( to | and |-)\d)|\d)|(\d,\d and \d)
You can use this pattern:
(Fig(?:ures?|s\.)) (\d+(?:(?:-|, | (?:and|to) )\d+)*)
If you need more flexibility, you can replace spaces with \h+ or \h*
edit:
I see my previous regex didn't work.
Atempting redemption, I offer two alternatives that do work -
1.
Using Multi-Line mode - This uses the \G anchor which provides a means
to get an aligned and trimmed output suitable for array
# '/(^Fig[a-zA-Z.]*\h+|(?!^)\G)(?(?<=\d)\h*,\h*)(\d+)(?|\h*(-)\h*(\d+)|\h+(and)\h+(\d+)|\h+(to)\h+(\d+))?/'
( # (1 start)
^ Fig [a-zA-Z.]* \h+ # Fig's
| # or,
(?! ^ ) # Start at the end of last match
\G
) # (1 end)
(?(?<= \d ) # Conditional, if previous digit
\h* , \h* # Require a comma
) # End conditional
( \d+ ) # (2), Digit
(?| # Branch reset (optionally, one of the (-|and|to) \d forms)
\h*
( - ) # (3), '-'
\h*
( \d+ ) # (4), Digit
| \h+
( and ) # (3), 'and'
\h+
( \d+ ) # (4), Digit
| \h+
( to ) # (3), 'to'
\h+
( \d+ ) # (4), Digit
)?
Perl test case
$/ = undef;
$str = <DATA>;
while ($str =~ /(^Fig[a-zA-Z.]*\h+|(?!^)\G)(?(?<=\d)\h*,\h*)(\d+)(?|\h*(-)\h*(\d+)|\h+(and)\h+(\d+)|\h+(to)\h+(\d+))?/mg)
{
length($1) ?
print "'$1'\t'$2'\t'$3'\t'$4'\n" :
print "'$1'\t\t'$2'\t'$3'\t'$4'\n" ;
}
__DATA__
Figs. 1, 2, 3 and 4
Figures 1, 2
Figs. 1 and 2
Figure 1-3
Figure 1 to 3
Figure 1
Output >>
'Figs. ' '1' '' ''
'' '2' '' ''
'' '3' 'and' '4'
'Figures ' '1' '' ''
'' '2' '' ''
'Figs. ' '1' 'and' '2'
'Figure ' '1' '-' '3'
'Figure ' '1' 'to' '3'
'Figure ' '1' '' ''
2. Using Multi-Line mode - This matches entire line, where capture group 1 contains 'Figs',
group 2 contains all the number forms
# '/^(Fig[a-zA-Z.]*\h+)((?(?<=\d)\h*,\h*|\d+(?:\h*-\h*\d+|\h+and\h+\d+|\h+to\h+\d+)?)+)\h*$/'
^
( Fig [a-zA-Z.]* \h+ ) # (1), Fig's
( # (2 start), All the num's
(?(?<= \d ) # Conditional, if previous digit
\h* , \h* # Require a comma
| # or
\d+ # Require a digit
(?: # (and optionally, one of the \d (-|and|to) \d forms)
\h* - \h* \d+
| \h+ and \h+ \d+
| \h+ to \h+ \d+
)?
)+ # End conditional, do many times
) # (2 end)
\h*
$
My search text is as follows.
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
It contains many lines(actually a javascript file) but need to parse the values in variable strings , ie aaa , bbb, ccc , ddd , eee
Following is the Perl code, or use PHP at bottom
my $str = <<STR;
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
STR
my #matches = $str =~ /(?:\"(.+?)\",?)/g;
print "#matches";
I know the above script will match all instants, but it will parse strings ("xyz") in the other lines also. So I need to check the string var strings =
/var strings = \[(?:\"(.+?)\",?)/g
Using above regex it will parse aaa.
/var strings = \[(?:\"(.+?)\",?)(?:\"(.+?)\",?)/g
Using above, will get aaa , and bbb. So to avoid the regex repeating I used '+' quantifier as below.
/var strings = \[(?:\"(.+?)\",?)+/g
But I got only eee, So my question is why I got eee ONLY when I used '+' quantifier?
Update 1: Using PHP preg_match_all (doing it to get more attention :-) )
$str = <<<STR
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
STR;
preg_match_all("/var strings = \[(?:\"(.+?)\",?)+/",$str,$matches);
print_r($matches);
Update 2: Why it matched eee ? Because of the greediness of (?:\"(.+?)\",?)+ . By removing greediness /var strings = \[(?:\"(.+?)\",?)+?/ aaa will be matched. But why only one result? Is there any way it can be achieved by using single regex?
Here's a single-regex solution:
/(?:\bvar\s+strings\s*=\s*\[|\G,)\s*"([^"]*)"/g
\G is a zero-width assertion that matches the position where the previous match ended (or the beginning of the string if it's the first match attempt). So this acts like:
var\s+strings\s*=\s*[\s*"([^"]*)"
...on the first attempt, then:
,\s*"([^"]*)"
...after that, but each match has to start exactly where the last one left off.
Here's a demo in PHP, but it will work in Perl, too.
You may prefer this solution which first looks for the string var strings = [ using the /g modifier. This sets \G to match immediately after the [ for the next regex, which looks for all immediately following occurrences of double-quoted strings, possibly preceded by commas or whitespace.
my #matches;
if ($str =~ /var \s+ strings \s* = \s* \[ /gx) {
#matches = $str =~ /\G [,\s]* "([^"]+)" /gx;
}
Despite using the /g modifier your regex /var strings = \[(?:\"(.+?)\",?)+/g matches only once because there is no second occurrence of var strings = [. Each match returns a list of the values of the capture variables $1, $2, $3 etc. when the match completed, and /(?:"(.+?)",?)+/ (there is no need to escape the double-quotes) captures multiple values into $1 leaving only the final value there. You need to write something like the above , which captures only a single value into $1 for each match.
Because the + tells it to repeat the exact stuff inside brackets (?:"(.+?)",?) one or more times. So it will match the "eee" string, end then look for repetitions of that "eee" string, which it does not find.
use YAPE::Regex::Explain;
print YAPE::Regex::Explain->new(qr/var strings = \[(?:"(.+?)",?)+/)->explain();
The regular expression:
(?-imsx:var strings = \[(?:"(.+?)",?)+)
matches as follows:
NODE EXPLANATION
----------------------------------------------------------------------
(?-imsx: group, but do not capture (case-sensitive)
(with ^ and $ matching normally) (with . not
matching \n) (matching whitespace and #
normally):
----------------------------------------------------------------------
var strings = 'var strings = '
----------------------------------------------------------------------
\[ '['
----------------------------------------------------------------------
(?: group, but do not capture (1 or more times
(matching the most amount possible)):
----------------------------------------------------------------------
" '"'
----------------------------------------------------------------------
( group and capture to \1:
----------------------------------------------------------------------
.+? any character except \n (1 or more
times (matching the least amount
possible))
----------------------------------------------------------------------
) end of \1
----------------------------------------------------------------------
" '"'
----------------------------------------------------------------------
,? ',' (optional (matching the most amount
possible))
----------------------------------------------------------------------
)+ end of grouping
----------------------------------------------------------------------
) end of grouping
----------------------------------------------------------------------
A simpler example would be:
my #m = ('abcd' =~ m/(\w)+/g);
print "#m";
Prints only d. This is due to:
use YAPE::Regex::Explain;
print YAPE::Regex::Explain->new(qr/(\w)+/)->explain();
The regular expression:
(?-imsx:(\w)+)
matches as follows:
NODE EXPLANATION
----------------------------------------------------------------------
(?-imsx: group, but do not capture (case-sensitive)
(with ^ and $ matching normally) (with . not
matching \n) (matching whitespace and #
normally):
----------------------------------------------------------------------
( group and capture to \1 (1 or more times
(matching the most amount possible)):
----------------------------------------------------------------------
\w word characters (a-z, A-Z, 0-9, _)
----------------------------------------------------------------------
)+ end of \1 (NOTE: because you are using a
quantifier on this capture, only the LAST
repetition of the captured pattern will be
stored in \1)
----------------------------------------------------------------------
) end of grouping
----------------------------------------------------------------------
If you use the quantifier on the capture group, only the last instance will be used.
Here's a way that works:
my $str = <<STR;
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
STR
my #matches;
$str =~ m/var strings = \[(.+?)\]/; # get the array first
my $jsarray = $1;
#matches = $array =~ m/"(.+?)"/g; # and get the strings from that
print "#matches";
Update:
A single-line solution (though not a single regex) would be:
#matches = ($str =~ m/var strings = \[(.+?)\]/)[0] =~ m/"(.+?)"/g;
But this is highly unreadable imho.
I realise that something similar has been asked before, but I can't seem to fit the solution to what I am trying to do, so please don't just think this is a dupe.
I have a string in the style {block:string}contents{/block:string}, which can be matched fairly easily with {block:([a-z_-\s]+)}.*{/block:\1}
What I want to do is modify the inner .* part so that it does not match any string that has a {block:[a-z_-\s]+} between it, that is all {block}{/block} that have a {block} inside them should not be matched.
Thanks!
Try
{block:([a-z_-\s]+)}[^{]*(?!{block:([a-z_-\s]+)}.*{\block:\2})[^}]*{/block:\1}
I am pretty mediocre at regex, but the negative lookahead bounded by the [^{]* and [^}]* statements should keep your matches tag-free.
Compressed: m~\{block:([a-z\s_-]+)\}(?:(?!\{/?block:\1\}).)*\{/block:\1\}~xs
Example in Perl:
$_ = '{block:string}conte{block:string}nts{/block:string}{/block:string}';
if ( m~ # match operator
\{block: ([a-z\s_-]+) \} # opening block structure and capt grp 1
(?: # begin non capt grp
(?! \{/?block: \1 \} ) # negative lookahead, don't want backreffed
# open or closed block struct
. # ok, grab this character
)* # end group, do 0 or more times (greedy)
\{/block: \1 \} # closing block structure matching grp 1
~xs ) # modifiers: expanded, include newlines
{
print "matched '$&'\n";
}
Output:
matched '{block:string}nts{/block:string}'
<?php
$ptn = "%(?:{block:[a-z_\s-]+})(?![^}]*?{block:).*?{/block:[a-z_\s-]+}%";
$str = "... your content here ...";
preg_match_all($ptn, $str, $matches);
print_r($matches);
?>
For example:
$str = "{block:string}test2{/block:string} {block:string}contents{block:string}{block:string}test3{/block:string}{/block:string}{/block:string} sdf ";
Would produce:
Array
(
[0] => Array
(
[0] => {block:string}test2{/block:string}
[1] => {block:string}test3{/block:string}
)
)