我正在开发一个C#项目,由于某种原因正逐渐转向使用正则表达式,但到目前为止,实现这一目标很痛苦。
具体来说,我需要在数学表达式中检查一元运算符,这可以在直接字符串搜索中与二元运算符混淆。
现在,该项目的正则表达式看起来像这样:
if str(pc) == player :
基本上,它假定运算符是一元的,它在二元运算符后面看。
即将尝试替换该正则表达式的当前代码看起来与此类似:
(?<=(?:<<|>>|\+|-|\*|\/|%|&&|\|\||&|\||\^|==|!=|<>|>=|=>|<=|=<|=|<|>)\s*|\A)(?:(?:\+|-|!|~)(?=\w|\())
问题是这段代码显然没有考虑到后视,它最终错误地将模糊的二元运算符(+, - )标记为一元。如何在没有正则表达式的情况下模拟外观?
编辑: 如果问题看起来过于宽泛,我只是要求一个更准确地模拟原始正则表达式的解决方案。只要它可以模拟特定的正则表达式,我就可以使用它。
编辑2: 上面的正则表达式目前匹配这样的运算符:
private static readonly string[] _unaryOps = new string[] { "+", "-", "!", "~" };
private string MatchUnaryOp(string expr, int index)
{
int foundIndex = int.MaxValue; // so currentIndex always starts out less than foundIndex
string foundOp = null;
foreach (string op in _unaryOps) {
int currentIndex = expr.IndexOf(op, index);
if (currentIndex > -1 && currentIndex < foundIndex) {
foundIndex = currentIndex;
foundOp = op;
}
}
return foundOp;
}
但是,上面的代码目前符合以下条件:
5 + -10
^ this minus sign matches the conditions in the regex
我想知道是否有一种可行的方法来解析这个简单的表达式。简单地确定减号是一元的,而加号则不是。有没有一个好方法来实现这个?可能通过循环遍历像这样的数组中的二进制操作?
5 + -10
^ the plus sign is incorrectly assumed to be unary
答案 0 :(得分:3)
不是通过从头开始创建解析引擎来重新发明轮子,而是重写正则表达式以完成您正在寻找的内容或提供有关您正在寻找的算法的更多详细信息建立,完成更全面的例子。
(?<=(?:<<|>>|\+|-|\*|\/|%|&&|\|\||&|\||\^|==|!=|<>|>=|=>|<=|=<|=|<|>)\s*|\A)(?:(?:\+|-|!|~)(?=\w|\())
通过用字符类替换一些替换并创建类似树的结构,可以提高正则表达式的效率。这将显着减少正则表达式引擎的回溯量。
如果交替进入\A
,那么在尝试匹配(?:(?:\+|-|!|~)(?=\w|\())
时表达式将失败。这是因为\A
表示字符串的结尾,因此根据定义,在结束后不能再有了。
这个简化版本的功能与你表达的相同,但工作量较少。请注意,此处的\A
仍会导致失败的匹配,如上所述。
(?<=(?:([&|<>=])\1|[-+*\/%&|^=<>]|!=|>=|=[<>]|<[>=])\s*|\A)(?:[-+!~](?=[\w(]))
答案 1 :(得分:0)
我找到了问题的解决方案。我所做的就是添加另一个参数,该参数代表最后一个匹配,或null
当它是字符串的开头时。然后检查该匹配先前是否为二元运算符或null
。如果不是,则该方法返回。
private static readonly string[] _unaryOps = new string[] { "+", "-", "!", "~" };
private static readonly string[] _binaryOps = new string[] { "<<", ">>", "+", "-", "*", "/", "%", "&&", "||", "&", "|", "^", "==", "!=", "<>", ">=", "=>", "<=", "=<", "=", "<", ">" };
private string MatchUnaryOp(string expr, int index, object lastMatch)
{
if (lastMatch != null && !_binaryOps.Contains(lastMatch.ToString())) {
return null; // the last match was not a binary operator or the beginning of a string, so this can't be unary.
}
int foundIndex = int.MaxValue; // so currentIndex always starts out less than foundIndex
string foundOp = null;
foreach (string op in _unaryOps) {
int currentIndex = expr.IndexOf(op, index);
if (currentIndex > -1 && currentIndex < foundIndex) {
foundIndex = currentIndex;
foundOp = op;
}
}
return foundOp;
}