Логические выражения для поиска в строке ( hello OR hi) AND world - C#
Формулировка задачи:
Может кто-то видел готовые библиотеки?
хотелось бы что-то пожирней и без напильника в комплекте
чтоб например изменять ключевые слова (ИЛИ И НЕ -- || && !), оба регистра кушала, с кавычками дружила, ошибки обходила и всяческие мелкие тонкости?
поисковики не понимают о чем речь
c# and not(VB or SQL)
Решение задачи: «Логические выражения для поиска в строке ( hello OR hi) AND world»
textual
Листинг программы
[Serializable]
public class StringExpression
{
private const string LBracket = "(";
private const string RBracket = ")";
private static Regex _splitRegex = new Regex(@"\b|(?<=[!&'""\(\)])|(?=[!&'""\(\)])", RegexOptions.Compiled);
private static Dictionary<TokenType, byte> _prio = new Dictionary<TokenType, byte>
{
{TokenType.OR, 1},
{TokenType.AND, 2},
{TokenType.NOT, 3},
//{TokenType.QUOTE, 4},
};
private static Dictionary<TokenType, List<string>> _keyWords = new Dictionary<TokenType, List<string>>
{
{TokenType.OR, new List<string>(3) {"OR", "|", "ИЛИ"}},
{TokenType.AND, new List<string>(3) {"AND", "&", "И"}},
{TokenType.NOT, new List<string>(3) {"NOT", "!", "НЕ"}},
{TokenType.Quote, new List<string>(3) {"\"", "'"}},
{TokenType.LBracket, new List<string>(1) {LBracket}},
{TokenType.RBracket, new List<string>(1) {RBracket}},
};
private string originExpression;
private bool ignoreCase;
private bool fullWords;
[XmlAttribute("IgnoreCase")]
public bool IgnoreCase
{
get { return ignoreCase; }
set { ignoreCase = value; }
}
[XmlAttribute("FullWords")]
public bool FullWords
{
get { return fullWords; }
set { fullWords = value; }
}
[XmlAttribute("Expression")]
public string Expression
{
get { return originExpression; }
set
{
originExpression = value;
tokenizedExpression = null;
}
}
public StringExpression()
:this(null)
{
}
public StringExpression(string expression, bool ignoreCase = true, bool fullWords = false)
{
this.fullWords = fullWords;
IgnoreCase = ignoreCase;
Expression = expression;
}
private List<string> tokenizedExpression;
private IEnumerable<string> Tokenized
{
get
{
if (tokenizedExpression == null)
{
tokenizedExpression = ToPostFix(originExpression);
}
return tokenizedExpression;
}
}
protected virtual List<string> ToPostFix(string sourceString)
{
if (string.IsNullOrEmpty(sourceString))
return new List<string>(0);
string[] tokens = _splitRegex.Split(sourceString);
if (tokens.Length == 1)
return new List<string>(1) { tokens[0] };
var result = new List<string>();
var stack = new Stack<string>();
bool isEscapedData = false;
var quotedData = new StringBuilder();
foreach (string rawToken in tokens)
{
if (string.IsNullOrEmpty(rawToken))
continue;
var token = rawToken.Trim().ToUpper();
TokenType typeToken = GetTokenType(token);
if (isEscapedData)
{
if (typeToken == TokenType.Quote)
{
result.Add(quotedData.ToString());
quotedData.Length = 0;
isEscapedData = false;
}
else
{
quotedData.Append(rawToken);
}
}
else
{
if (StringExt.IsNullOrWhiteSpace(token))
continue;
switch (typeToken)
{
case TokenType.AND:
case TokenType.NOT:
case TokenType.OR:
while (stack.Count > 0)
{
string topToken = stack.Peek();
if (_prio.ContainsKey(GetTokenType(topToken)) &&
_prio[typeToken] < _prio[GetTokenType(topToken)])
{
result.Add(stack.Pop());
}
else
{
break;
}
result.Add(stack.Pop());
}
stack.Push(token);
break;
case TokenType.LBracket:
stack.Push(rawToken);
break;
case TokenType.RBracket:
if (stack.Count <= 0)
throw new Exception("Некорректное выражение");
string op = stack.Pop();
while (op != LBracket)
{
result.Add(op);
if (stack.Count <= 0)
throw new Exception("Некорректное количевство скобок");
op = stack.Pop();
}
break;
case TokenType.Quote:
isEscapedData = true;
break;
default:
result.Add(rawToken);
break;
}
}
}
if (isEscapedData)
{
throw new Exception("Кавычка не закрыта");
}
foreach (string symbol in stack)
{
result.Add(symbol);
}
return result;
}
public virtual bool IsMatch(string text)
{
if (text == null)
throw new ArgumentNullException("text");
var stackResult = new Stack<bool>();
if (!Tokenized.Any())
return true;
foreach (var token in Tokenized)
{
if (StringExt.IsNullOrWhiteSpace(token))
continue;
bool left;
bool right;
switch (GetTokenType(token))
{
case TokenType.AND:
left = stackResult.Pop();
right = stackResult.Pop();
stackResult.Push(left && right);
break;
case TokenType.NOT:
left = stackResult.Pop();
stackResult.Push(!left);
break;
case TokenType.OR:
left = stackResult.Pop();
right = stackResult.Pop();
stackResult.Push(left || right);
break;
case TokenType.LBracket:
break;
case TokenType.RBracket:
break;
case TokenType.Quote:
break;
default:
bool exists;
if (fullWords)
{
exists = Regex.IsMatch(text, @"\b" + token + @"\b",
ignoreCase
? RegexOptions.IgnoreCase
: RegexOptions.None);
}
else
{
exists = text.IndexOf(token, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) >= 0;
}
stackResult.Push(exists);
break;
}
}
if (stackResult.Count != 1)
{
throw new Exception("Ошибки при использовании выражения");
}
return stackResult.Pop();
}
private TokenType GetTokenType(string token)
{
if (StringExt.IsNullOrWhiteSpace(token))
return TokenType.String;
foreach (var keyWord in _keyWords)
{
if (keyWord.Value.Contains(token))
{
return keyWord.Key;
}
}
return TokenType.String;
}
public enum TokenType
{
NOT,
AND,
OR,
LBracket,
RBracket,
Quote,
String
}
private static class StringExt
{
public static bool IsNullOrWhiteSpace(string str)
{
if (string.IsNullOrEmpty(str))
return true;
for (int i = 0; i < str.Length; i++)
{
if (!char.IsWhiteSpace(str[i]))
{
return false;
}
}
return true;
}
}
}