I am trying to make various regular expressions for a basic calculator. The string will be composed of characters + - * /
and integers, following the following structure.
n1 + n2 – n3 * n4 / n5 …
The objective is that it allows me to introduce only operations of + -
and figures of 2 digits in grade 1 and in the others that it allows me the 4 basic operations + - * /
and figures of more digits.
Code
private void TextBoxOperation_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var text = TextBoxOperation.Text + e.Text;
if (SelectedGrade == 1)
{
var regexItem = new Regex(@"[ ]{0,1}[0-9]{1,2}[-+ ]{0,2}");
e.Handled = !regexItem.IsMatch(text);
}
else if (SelectedGrade == 2)
{
var regexItem = new Regex(@"[ ]{0,1}[0-9]{1,3}[-+*\/ ]{0,2}");
e.Handled = !regexItem.IsMatch(text);
}
//...
}
1st grade
Entrada | Salida
n + n | true
n - n | true
n * n | false
n / n | false
nn + n - n | true
n + nn - n | true
n + n - nn | true
nnn + n - n | false
nnnn + n - n| false
2nd grade
Entrada | Salida
n + n | true
n - n | true
n * n | true
n / n | true
nn + n - n | true
n + nn - n | true
n + n - nn | true
nnn + n - n | true
nnnn + n - n| false
Update
In case of using [0-9*+-\/ ]
it always returns true . The problem is that it allows me to type multiple characters ++, +-, ...
, multiple spaces and I have no control over the number of figures. I've also noticed that e.Text only returns the last char .
Reward
If apart from the above, it also allows spaces and parentheses.
Entrada | Salida
) | false
( | true
(+ | false
( + | false
() | false
( n | true
( n + | true
(n+n) | true
( n + n ) | true
nn + (n – n) | true
(n + nn) – n | true
(n + n) – nn)) | false
((n + n) – nn)) | true
I hope someone can help me. Thanks.
Solution with regular expressions
Try the following regular expression for the first degree case. Allow balanced parentheses and spaces:
^(?!.*\([ \t]*\))(?!.*[+-][ \t]*[+-])(?!.*\([ \t]*[+-])(?!.*[+-][ \t]*\))(?:[+-]|\b\d{1,2}\b|[ \t])*(?:(?:(?'open'\()(?:[+-]|\b\d{1,2}\b|[ \t])*)+(?:(?'-open'\)?)(?:[+-]|\b\d{1,2}\b|[ \t])*)+)*\s*$
You have a demo here .
For the second degree case, you can use this other one:
^(?!.*\([ \t]*\))(?!.*[+/*-][ \t]*[+/*-])(?!.*\([ \t]*[+/*-])(?!.*[+/*-][ \t]*\))(?:[+/*-]|\b\d{1,3}\b|[ \t])*(?:(?:(?'open'\()(?:[+/*-]|\b\d{1,3}\b|[ \t])*)+(?:(?'-open'\)?)(?:[+/*-]|\b\d{1,3}\b|[ \t])*)+)*\s*$
Explanation:
Both are based on the "balanced groups" functionality of .NET regular expressions. I recommend this read on balanced groups. Where they will explain themselves much better than I can here.
On that same page a regular expression is proposed to search for balanced parentheses with any character (other than parentheses) in between.
The expression is this:
^[^()]*(?>(?>(?'open'\()[^()]*)+(?>(?'-open'\))[^()]*)+)+(?(open)(?!))$
Here we will make some change. We'll make the optional closing parenthesis first, since our text may be valid as it is typed. In addition, we will delete the conditional , which aborts (via a lookahead that always fails) if it finds more open than closed parentheses.
Also, since the closing parenthesis is now optional, we will change the atomic groups to normal groups, since we will need to be able to do backtracking.
So, after the changes it looks something like this:
^[^()]*(?:(?:(?'open'\()[^()]*)+(?:(?'-open'\)?)[^()]*)+)+$
But you have to continue with the changes:
Instead of allowing anything between the parentheses, what we will do is allow one of these options:
[+-]
[\t ]
\b\d{1,2}\b
All that can be repeated from 0 to more times, so between the parentheses we can get things like
1+1+1-2
, but also wrong things like+++++
. But we will solve this later.At the moment the regular expression looks like this:
^(?:[+-]|\b\d{1,2}\b|[ \t])*(?:(?:(?'open'\()(?:[+-]|\b\d{1,2}\b|[ \t])*)+(?:(?'-open'\)?)(?:[+-]|\b\d{1,2}\b|[ \t])*)+)*$
Finally, to avoid the possible incorrect content mentioned above, we will add validations made with lookaheads at the beginning of the regular expression. We will be excluding all the incorrect cases that may occur. What:
(?!.*\([ \t]*\))
(?!.*[+-][ \t]*[+-])
(?!.*\([ \t]*[+-])
(?!.*[+-][ \t]*\))
This is how we get the resulting final regular expression:
^(?!.*\([ \t]*\))(?!.*[+-][ \t]*[+-])(?!.*\([ \t]*[+-])(?!.*[+-][ \t]*\))(?:[+-]|\b\d{1,2}\b|[ \t])*(?:(?:(?'open'\()(?:[+-]|\b\d{1,2}\b|[ \t])*)+(?:(?'-open'\)?)(?:[+-]|\b\d{1,2}\b|[ \t])*)+)*\s*$
For the second regular expression, we change all
[+-]
to[+/*-]
to allow the rest of the operations. We will also change the\b{1,2}\b
by\b{1,3}\b
to allow numbers up to three digits.