Validate a Boolean expression with brackets in C# - c#
I want to validate a string in C# that contains a Boolean expression with brackets.
The string should only contain numbers 1-9, round brackets, "OR" , "AND".
Examples of good strings:
"1 AND 2"
"2 OR 4"
"4 AND (3 OR 5)"
"2"
And so on...
I am not sure if Regular Expression are flexible enough for this task.
Is there a nice short way of achieving this in C# ?
It's probably simpler to do this with a simple parser. But you can do this with .NET regex by using balancing groups and realizing that if the brackets are removed from the string you always have a string matched by a simple expression like ^\d+(?:\s+(?:AND|OR)\s+\d+)*\z.
So all you have to do is use balancing groups to make sure that the brackets are balanced (and are in the right place in the right form).
Rewriting the expression above a bit:
(?x)^
OPENING
\d+
CLOSING
(?:
\s+(?:AND|OR)\s+
OPENING
\d+
CLOSING
)*
BALANCED
\z
((?x) makes the regex engine ignore all whitespace and comments in the pattern, so it can be made more readable.)
Where OPENING matches any number (0 included) of opening brackets:
\s* (?: (?<open> \( ) \s* )*
CLOSING matches any number of closing brackets also making sure that the balancing group is balanced:
\s* (?: (?<-open> \) ) \s* )*
and BALANCED performs a balancing check, failing if there are more open brackets then closed:
(?(open)(?!))
Giving the expression:
(?x)^
\s* (?: (?<open> \( ) \s* )*
\d+
\s* (?: (?<-open> \) ) \s* )*
(?:
\s+(?:AND|OR)\s+
\s* (?: (?<open> \( ) \s* )*
\d+
\s* (?: (?<-open> \) ) \s* )*
)*
(?(open)(?!))
\z
If you do not want to allow random spaces remove every \s*.
Example
See demo at IdeOne. Output:
matched: '2'
matched: '1 AND 2'
matched: '12 OR 234'
matched: '(1) AND (2)'
matched: '(((1)) AND (2))'
matched: '1 AND 2 AND 3'
matched: '1 AND (2 OR (3 AND 4))'
matched: '1 AND (2 OR 3) AND 4'
matched: ' ( 1 AND ( 2 OR ( 3 AND 4 ) )'
matched: '((1 AND 7) OR 6) AND ((2 AND 5) OR (3 AND 4))'
matched: '(1)'
matched: '(((1)))'
failed: '1 2'
failed: '1(2)'
failed: '(1)(2)'
failed: 'AND'
failed: '1 AND'
failed: '(1 AND 2'
failed: '1 AND 2)'
failed: '1 (AND) 2'
failed: '(1 AND 2))'
failed: '(1) AND 2)'
failed: '(1)() AND (2)'
failed: '((1 AND 7) OR 6) AND (2 AND 5) OR (3 AND 4))'
failed: '((1 AND 7) OR 6) AND ((2 AND 5 OR (3 AND 4))'
failed: ''
If you consider a boolean expression as generated by a formal grammar writing a parser is easier.
I made an open source library to interpret simple boolean expressions. You can take a look at it on GitHub, in particular look at the AstParser class and Lexer.
If you just want to validate the input string, you can write a simple parser.
Each method consumes a certain kind of input (digit, brackets, operator) and returns the remaining string after matching. An exception is thrown if no match can be made.
public class ParseException : Exception { }
public static class ExprValidator
{
public static bool Validate(string str)
{
try
{
string term = Term(str);
string stripTrailing = Whitespace(term);
return stripTrailing.Length == 0;
}
catch(ParseException) { return false; }
}
static string Term(string str)
{
if(str == string.Empty) return str;
char current = str[0];
if(current == '(')
{
string term = LBracket(str);
string rBracket = Term(term);
string temp = Whitespace(rBracket);
return RBracket(temp);
}
else if(Char.IsDigit(current))
{
string rest = Digit(str);
try
{
//possibly match op term
string op = Op(rest);
return Term(op);
}
catch(ParseException) { return rest; }
}
else if(Char.IsWhiteSpace(current))
{
string temp = Whitespace(str);
return Term(temp);
}
else throw new ParseException();
}
static string Op(string str)
{
string t1 = Whitespace_(str);
string op = MatchOp(t1);
return Whitespace_(op);
}
static string MatchOp(string str)
{
if(str.StartsWith("AND")) return str.Substring(3);
else if(str.StartsWith("OR")) return str.Substring(2);
else throw new ParseException();
}
static string LBracket(string str)
{
return MatchChar('(')(str);
}
static string RBracket(string str)
{
return MatchChar(')')(str);
}
static string Digit(string str)
{
return MatchChar(Char.IsDigit)(str);
}
static string Whitespace(string str)
{
if(str == string.Empty) return str;
int i = 0;
while(i < str.Length && Char.IsWhiteSpace(str[i])) { i++; }
return str.Substring(i);
}
//match at least one whitespace character
static string Whitespace_(string str)
{
string stripFirst = MatchChar(Char.IsWhiteSpace)(str);
return Whitespace(stripFirst);
}
static Func<string, string> MatchChar(char c)
{
return MatchChar(chr => chr == c);
}
static Func<string, string> MatchChar(Func<char, bool> pred)
{
return input => {
if(input == string.Empty) throw new ParseException();
else if(pred(input[0])) return input.Substring(1);
else throw new ParseException();
};
}
}
Pretty simply:
At first stage you must determ lexems (digit, bracket or operator) with simple string comparsion.
At second stage you must define variable of count of closed bracket (bracketPairs), which can be calculated by the following algorithm for each lexem:
if current lexem is '(', then bracketPairs++;
if current lexem is ')', then bracketPairs--.
Else do not modify bracketPairs.
At the end if all lexems are known and bracketPairs == 0 then input expression is valid.
The task is a bit more complex, if it's necesery to build AST.
what you want are "balanced groups", with them you can get all bracet definitions, then you just need a simple string parsing
http://blog.stevenlevithan.com/archives/balancing-groups
http://msdn.microsoft.com/en-us/library/bs2twtah.aspx#balancing_group_definition
ANTLR Parser Generator?
a short way of achieving this in C#
Although it may be an overkill if its just numbers and OR + AND
Related
Building a regular expression in C#
How to check the following text in C# with Regex: key_in-get { 43243225543543543 }; or key_in_set { password123 : 34980430943834 }; I tried to build a regular expression, but I failed after few hours. Here is my code: string text1 = "key_in-get { 322389238237 };"; string text2 = "key_in-set { password123 : 322389238237 };"; string pattern = "key_in-(get|set) { .* };"; var result1 = Regex.IsMatch(text, pattern); Console.Write("Is valid: {0} ", result1); var result2 = Regex.IsMatch(text, pattern); Console.Write("Is valid: {0} ", result2); I have to check if there is "set" or "get". If the pattern finds "set" then it can only accept following pattern "text123 : 123456789", and if it finds "get" then should accept only "123456789".
You can use key_in-(?:get|(set)) {(?(1) \w+ :) \w+ }; key_in-(?:get|(set))\s*{(?(1)\s*\w+\s*:)\s*\w+\s*}; key_in-(?:get|(set))\s*{(?(1)\s*\w+\s*:)\s*\d+\s*}; See the regex demo. The second one allows any amount of any whitespace between the elements and the third one allows only digits after : or as part of the get expression. If the whole string must match, add ^ at the start and $ at the end of the pattern. Details: key_in- - a substring (?:get|(set)) - get or set (the latter is captured into Group 1) \s* - zero or more whitespaces { - a { char (?(1)\s*\w+\s*:) - a conditional construct: if Group 1 matched, match one or more word chars enclosed with zero or more whitespaces and then a colon \s*\w+\s* - one or more word chars enclosed with zero or more whitespaces }; - a literal substring.
In the pattern that you tried key_in-(get|set) { .* }; you are matching either get or set followed by { until the last occurrence of } which could possibly also match key_in-get { }; }; As an alternative solution, you could use an alternation | specifying each of the accepted parts for the get and the set. key_in-(?:get\s*{\s*\w+|set\s*{\s*\w+\s*:\s*\w+)\s*}; The pattern matches key_in- Match literally (?: Non capture group get\s*{\s*\w+ Match get, { between optional whitespace chars and 1+ word chars | Or set\s*{\s*\w+\s*:\s*\w+ Match set, { between optional whitespace chars and word chars on either side with : in between. ) Close non capture group \s*}; Match optional whitespace chars and }; Regex demo
How to not match a string instance with a line break? [duplicate]
This question already has answers here: How do I match an entire string with a regex? (8 answers) Closed 2 years ago. I am new to using Regex and was struggling with a problem. I have got to a certain point and I am stuck, I can't figure out how to 'ignore' or not match a pin which has a line break in it which is causing the code to fail the test. using System; using System.Text.RegularExpressions; public class Pin { public static bool ValidatePin(string pin){ string pattern = "^([0-9]{4}|[0-9]{6})$"; Regex reg = new Regex(pattern); Match match = reg.Match(pin); if (match.Success) { return true; } else { return false; } I have the regular expression above, how would I implement it so that when it tries to match a pin with the line break it returns "FALSE". The failed test pin was: "1234\n".
Replace $ (it can match before the line feed symbol at the string end position) with \z, the very end of string, use string pattern = #"\A([0-9]{4}|[0-9]{6})\z"; \A is also useful instead of ^ so as to always match the start of string (if that's your intention). See proof. Explanation -------------------------------------------------------------------------------- \A the beginning of the string -------------------------------------------------------------------------------- ( group and capture to \1: -------------------------------------------------------------------------------- [0-9]{4} any character of: '0' to '9' (4 times) -------------------------------------------------------------------------------- | OR -------------------------------------------------------------------------------- [0-9]{6} any character of: '0' to '9' (6 times) -------------------------------------------------------------------------------- ) end of \1 -------------------------------------------------------------------------------- \z the end of the string
Regex for alphanumeric, at least 1 number and special chars
I am trying to find a regex which will give me the following validation: string should contain at least 1 digit and at least 1 special character. Does allow alphanumeric. I tried the following but this fails: #"^[a-zA-Z0-9##$%&*+\-_(),+':;?.,!\[\]\s\\/]+$]" I tried "password1$" but that failed I also tried "Password1!" but that also failed. ideas? UPDATE Need the solution to work with C# - currently the suggestions posted as of Oct 22 2013 do not appear to work.
Try this: Regex rxPassword = new Regex( #" ^ # start-of-line, followed by [a-zA-Z0-9!##]+ # a sequence of one or more characters drawn from the set consisting of ASCII letters, digits or the punctuation characters ! # and # (<=[0-9]) # at least one of which is a decimal digit (<=[!##]) # at least one of which is one of the special characters (<=[a-zA-Z]) # at least one of which is an upper- or lower-case letter $ # followed by end-of-line " , RegexOptions.IgnorePatternWhitespace ) ; The construct (<=regular-expression) is a zero-width positive look-behind assertion.
Sometimes it's a lot simpler to do things one step at a time. The static constructor builds the escaped character class characters from a simple list of allowed special characters. The built-in Regex.Escape method doesn't work here. public static class PasswordValidator { private const string ALLOWED_SPECIAL_CHARS = #"##$%&*+_()':;?.,![]\-"; private static string ESCAPED_SPECIAL_CHARS; static PasswordValidator() { var escapedChars = new List<char>(); foreach (char c in ALLOWED_SPECIAL_CHARS) { if (c == '[' || c == ']' || c == '\\' || c == '-') escapedChars.AddRange(new[] { '\\', c }); else escapedChars.Add(c); } ESCAPED_SPECIAL_CHARS = new string(escapedChars.ToArray()); } public static bool IsValidPassword(string input) { // Length requirement? if (input.Length < 8) return false; // First just check for a digit if (!Regex.IsMatch(input, #"\d")) return false; // Then check for special character if (!Regex.IsMatch(input, "[" + ESCAPED_SPECIAL_CHARS + "]")) return false; // Require a letter? if (!Regex.IsMatch(input, "[a-zA-Z]")) return false; // DON'T allow anything else: if (Regex.IsMatch(input, #"[^a-zA-Z\d" + ESCAPED_SPECIAL_CHARS + "]")) return false; return true; } }
This may be work, there are two possible, the digit before special char or the digit after the special char. You should use DOTALL(the dot point all char) ^((.*?[0-9].*?[##$%&*+\-_(),+':;?.,!\[\]\s\\/].*)|(.*?[##$%&*+\-_(),+':;?.,!\[\]\s\\/].*?[0-9].*))$
This worked for me: #"(?=^[!##$%\^&*()_-+=[{]};:<>|./?a-zA-Z\d]{8,}$)(?=([!##$%\^&*()_-+=[{]};:<>|./?a-zA-Z\d]\W+){1,})(?=[^0-9][0-9])[!##$%\^&*()_-+=[{]};:<>|./?a-zA-Z\d]*$" alphanumeric, at least 1 numeric, and special character with a min length of 8
This should do the work (?:(?=.*[0-9]+)(?=.*[a-zA-Z]+)(?=.*[##$%&*+\-_(),+':;?.,!\[\]\s\\/]+))+ Tested with javascript, not sure about c#, may need some little adjust. What it does is use anticipated positive lookahead to find the required elements of the password. EDIT Regular expression is designed to test if there are matches. Since all the patterns are lookahead, no real characters get captured and matches are empty, but if the expression "match", then the password is valid. But, since the question is C# (sorry, i don't know c#, just improvising and adapting samples) string input = "password1!"; string pattern = #"^(?:(?=.*[0-9]+)(?=.*[a-zA-Z]+)(?=.*[##$%&*+\-_(),+':;?.,!\[\]\s\\/]+))+.*$"; Regex rgx = new Regex(pattern, RegexOptions.None); MatchCollection matches = rgx.Matches(input); if (matches.Count > 0) { Console.WriteLine("{0} ({1} matches):", input, matches.Count); foreach (Match match in matches) Console.WriteLine(" " + match.Value); } Adding start of line, and a .*$ to the end, the expression will match if the password is valid. And the match value will be the password. (i guess)
regex how to extract strings between delimiters and not double delimiters
I'm sure this is a duplicate but cannot find the right search criteria. Basically I have a user supplied string with keywords enclosed in braces, I want a regex that will find the keywords but will ignore double sets of the delimiters. example: "A cat in a {{hat}} doesn't bite {back}." I need regex that will return {back} but not {hat}. This is for C#.
This is what you are looking for (?<!\{)\{\w+\}(?!\})
The answer will vary slightly depending on which regex parser you are using, but something like the following is probably what you want: (?:[^{]){([^}]*)}|{([^}]*)}(?:[^}])|^{([^}]*)}$ Non "{" (not part of the match) followed by "{" (capturing) all the non "}" chars (end capturing) followed by "}", or... "{" followed by "{" (capturing) all the non "}" chars (end capturing) followed by non "}" (not part of the match), or... Start-of-line followed by "{" (capturing) all the non "}" chars (end capturing) followed by end-of-line Note that some parsers may not recognize the "?:" operator and some parsers may require that some or all of the following chars (when not inside of "[]") be backslash escaped: { } ( ) |
Description Try this out, this will require the open and close brackets to be single. Double brackets will be ignored. See also this permlink example [^{][{]([^}]*)[}][^}] c# example using System; using System.Text.RegularExpressions; namespace myapp { class Class1 { static void Main(string[] args) { String sourcestring = "A cat in a {{hat}} doesn't bite {back}."; Regex re = new Regex(#"[^{][{]([^}]*)[}][^}]"); MatchCollection mc = re.Matches(sourcestring); int mIdx=0; foreach (Match m in mc) { for (int gIdx = 0; gIdx < m.Groups.Count; gIdx++) { Console.WriteLine("[{0}][{1}] = {2}", mIdx, re.GetGroupNames()[gIdx], m.Groups[gIdx].Value); } mIdx++; } } } } $matches Array: ( [0] => Array ( [0] => {back}. ) [1] => Array ( [0] => back ) )
Not too hard just used a Regex helper : (?:[^{]){([^}]*)}|{([^}]*)}(?:[^}])|^{([^}]*)}$
How to parse a comma delimited string when comma and parenthesis exists in field
I have this string in C# adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO I want to use a RegEx to parse it to get the following: adj_con(CL2,1,3,0) adj_cont(CL1,1,3,0) NG NG/CL 5 value of CL(JK) HO In addition to the above example, I tested with the following, but am still unable to parse it correctly. "%exc.uns: 8 hours let # = ABC, DEF", "exc_it = 1 day" , " summ=graffe ", " a,b,(c,d)" The new text will be in one string string mystr = #"""%exc.uns: 8 hours let # = ABC, DEF"", ""exc_it = 1 day"" , "" summ=graffe "", "" a,b,(c,d)""";
string str = "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO"; var resultStrings = new List<string>(); int? firstIndex = null; int scopeLevel = 0; for (int i = 0; i < str.Length; i++) { if (str[i] == ',' && scopeLevel == 0) { resultStrings.Add(str.Substring(firstIndex.GetValueOrDefault(), i - firstIndex.GetValueOrDefault())); firstIndex = i + 1; } else if (str[i] == '(') scopeLevel++; else if (str[i] == ')') scopeLevel--; } resultStrings.Add(str.Substring(firstIndex.GetValueOrDefault()));
Event faster: ([^,]*\x28[^\x29]*\x29|[^,]+) That should do the trick. Basically, look for either a "function thumbprint" or anything without a comma. adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO ^ ^ ^ ^ ^ The Carets symbolize where the grouping stops.
Just this regex: [^,()]+(\([^()]*\))? A test example: var s= "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO"; Regex regex = new Regex(#"[^,()]+(\([^()]*\))?"); var matches = regex.Matches(s) .Cast<Match>() .Select(m => m.Value); returns adj_con(CL2,1,3,0) adj_cont(CL1,1,3,0) NG NG/CL 5 value of CL(JK) HO
If you simply must use Regex, then you can split the string on the following: , # match a comma (?= # that is followed by (?: # either [^\(\)]* # no parens at all | # or (?: # [^\(\)]* # ... \( # ( [^\(\)]* # stuff in parens \) # ) [^\(\)]* # ... )+ # any number of times )$ # until the end of the string ) It breaks your input into the following: adj_con(CL2,1,3,0) adj_cont(CL1,1,3,0) NG NG/CL 5 value of CL(JK) HO You can also use .NET's balanced grouping constructs to create a version that works with nested parens, but you're probably just as well off with one of the non-Regex solutions.
Another way to implement what Snowbear was doing: public static string[] SplitNest(this string s, char src, string nest, string trg) { int scope = 0; if (trg == null || nest == null) return null; if (trg.Length == 0 || nest.Length < 2) return null; if (trg.IndexOf(src) >= 0) return null; if (nest.IndexOf(src) >= 0) return null; for (int i = 0; i < s.Length; i++) { if (s[i] == src && scope == 0) { s = s.Remove(i, 1).Insert(i, trg); } else if (s[i] == nest[0]) scope++; else if (s[i] == nest[1]) scope--; } return s.Split(trg); } The idea is to replace any non-nested delimiter with another delimiter that you can then use with an ordinary string.Split(). You can also choose what type of bracket to use - (), <>, [], or even something weird like \/, ][, or `'. For your purposes you would use string str = "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO"; string[] result = str.SplitNest(',',"()","~"); The function would first turn your string into adj_con(CL2,1,3,0)~adj_cont(CL1,1,3,0)~NG~ NG/CL~ 5 value of CL(JK)~ HO then split on the ~, ignoring the nested commas.
Assuming non nested, matching parentheses, you can easily match the tokens you want instead of splitting the string: MatchCollection matches = Regex.Matches(data, #"(?:[^(),]|\([^)]*\))+");
var s = "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO"; var result = string.Join(#"\n",Regex.Split(s, #"(?<=\)),|,\s")); The pattern matches for ) and excludes it from the match then matches , or matches , followed by a space. result = adj_con(CL2,1,3,0) adj_cont(CL1,1,3,0) NG NG/CL 5 value of CL(JK) HO
The TextFieldParser (msdn) class seems to have the functionality built-in: TextFieldParser Class: - Provides methods and properties for parsing structured text files. Parsing a text file with the TextFieldParser is similar to iterating over a text file, while the ReadFields method to extract fields of text is similar to splitting the strings. The TextFieldParser can parse two types of files: delimited or fixed-width. Some properties, such as Delimiters and HasFieldsEnclosedInQuotes are meaningful only when working with delimited files, while the FieldWidths property is meaningful only when working with fixed-width files. See the article which helped me find that
Here's a stronger option, which parses the whole text, including nested parentheses: string pattern = #" \A (?> (?<Token> (?: [^,()] # Regular character | (?<Paren> \( ) # Opening paren - push to stack | (?<-Paren> \) ) # Closing paren - pop | (?(Paren),) # If inside parentheses, match comma. )*? ) (?(Paren)(?!)) # If we are not inside parentheses, (?:,|\Z) # match a comma or the end )*? # lazy just to avoid an extra empty match at the end, # though it removes a last empty token. \Z "; Match match = Regex.Match(data, pattern, RegexOptions.IgnorePatternWhitespace); You can get all matches by iterating over match.Groups["Token"].Captures.