문자열 평가 "3*(4+2)"수율 int 18 [복제
문제
이 질문은 이미 여기에 답이 있습니다.
문자열에 포함 된 숫자 표현식을 평가하고 결과를 반환 할 수있는 .NET 프레임 워크 기능이 있습니까? Fe :
string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18
내 대체 할 수있는 표준 프레임 워크 기능이 있습니까? EvaluateExpression
방법?
해결책
예, C# 컴파일러가 런타임에 평가할 수 있습니다.
보다: Csharpcorner
다른 팁
문자열 표현식을 평가하려면 아래 코드 스 니펫을 사용하십시오.
using System.Data;
DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
컴파일러를 사용하면 생성 된 어셈블리가로드되어 해제되지 않으므로 메모리 누출이 의미합니다. 실제 표현 통역사를 사용하는 것보다 성능이 떨어집니다. 이를 위해 사용할 수 있습니다 NCALC 이것은 전적으로 의도적 인 오픈 소스 프레임 워크입니다. 이미 포함 된 변수가 충분하지 않은 경우 자신의 변수와 사용자 정의 기능을 정의 할 수도 있습니다.
예시:
Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
이 시도:
static double Evaluate(string expression) {
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof (double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double) (loDataTable.Rows[0]["Eval"]);
}
당신은 "xpathnavigator.evaluate"를 볼 수 있습니다. 나는 이것을 내 그리드 뷰의 수학적 표현을 처리하는 데 사용했으며 그것은 나에게 잘 작동합니다.
다음은 제 프로그램에 사용한 코드입니다.
public static double Evaluate(string expression)
{
return (double)new System.Xml.XPath.XPathDocument
(new StringReader("<r/>")).CreateNavigator().Evaluate
(string.Format("number({0})", new
System.Text.RegularExpressions.Regex(@"([\+\-\*])")
.Replace(expression, " ${1} ")
.Replace("/", " div ")
.Replace("%", " mod ")));
}
static double Evaluate(string expression) {
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof (double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double) (loDataTable.Rows[0]["Eval"]);
}
작동 방식에 대한 설명 :
먼저, 우리는 그 부분에 테이블을 만듭니다 var loDataTable = new DataTable();
, 데이터베이스 엔진과 마찬가지로 (예 : MS SQL).
그런 다음 일부 매개 변수가있는 열이 있습니다 (var loDataColumn = new DataColumn("Eval", typeof (double), expression);
).
그만큼 "Eval"
매개 변수는 열의 이름입니다 (columnName 속성).
typeof (double)
열에 저장 될 데이터 유형이며, 이는 Put과 동일합니다. System.Type.GetType("System.Double");
대신에.
expression
문자열입니다 Evaluate
메소드는 수신하고 속성에 저장됩니다 Expression
열의. 이 속성은 실제로 구체적인 목적 (명백한)을위한 것입니다. 이는 열에 넣는 모든 행이 "표현식"으로 완성 될 것이며, 실제로는 SQL 쿼리에 넣을 수 있습니다. 인용하다 http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx 표현식 속성에 무엇을 넣을 수 있는지, 어떻게 평가되는지 알기 위해.
그 다음에, loDataTable.Columns.Add(loDataColumn);
열을 추가합니다 loDataColumn
~로 loDataTable
테이블.
그런 다음, 표현식 속성이있는 개인화 된 열이있는 행이 테이블에 추가됩니다. loDataTable.Rows.Add(0);
. 이 행을 추가하면 테이블의 "평가"열의 셀 loDataTable
"expression"속성으로 자동으로 채워지고, 연산자와 SQL 쿼리 등이있는 경우 평가 된 다음 셀에 저장되므로 "마법"이 발생합니다. 셀...
마지막으로, 저장된 값을 행 0에서 "평가"열의 셀에 반환합니다 (인덱스, 0에서 계산하기 시작). return (double) (loDataTable.Rows[0]["Eval"]);
.
그리고 그게 다 ... 일이 끝났어!
그리고 여기에 동일하게 이해하는 코드 eaiser는 ... 메소드 내부에 있지 않으며 설명됩니다.
DataTable MyTable = new DataTable();
DataColumn MyColumn = new DataColumn();
MyColumn.ColumnName = "MyColumn";
MyColumn.Expression = "5+5/5"
MyColumn.DataType = typeof(double);
MyTable.Columns.Add(MyColumn);
DataRow MyRow = MyTable.NewRow();
MyTable.Rows.Add(MyRow);
return (double)(MyTable.Rows[0]["MyColumn"]);
먼저, 테이블을 만듭니다 DataTable MyTable = new DataTable();
그런 다음 열이 있습니다 DataColumn MyColumn = new DataColumn();
다음으로, 우리는 열에 이름을 넣었습니다. 이것은 테이블에 저장 될 때 내용을 검색 할 수 있습니다. 완료 MyColumn.ColumnName = "MyColumn";
그런 다음 표현식, 여기에 유형 문자열 변수를 넣을 수 있습니다.이 경우 사전 정의 된 문자열 "5+5/5"가 있습니다.
열에 저장할 데이터 유형 MyColumn.DataType = typeof(double);
기둥을 테이블에 추가 ... MyTable.Columns.Add(MyColumn);
테이블 구조를 복사하는 테이블에 삽입 할 행을 만듭니다. DataRow MyRow = MyTable.NewRow();
테이블에 행을 추가하십시오 MyTable.Rows.Add(MyRow);
열의 행 0 행에서 셀의 값을 반환합니다. MyColumn
테이블의 MyTable
~와 함께 return (double)(MyTable.Rows[0]["MyColumn"]);
수업 완료 !!!
이것은 스택을 사용하는 간단한 표현 평가자입니다
public class MathEvaluator
{
public static void Run()
{
Eval("(1+2)");
Eval("5*4/2");
Eval("((3+5)-6)");
}
public static void Eval(string input)
{
var ans = Evaluate(input);
Console.WriteLine(input + " = " + ans);
}
public static double Evaluate(String input)
{
String expr = "(" + input + ")";
Stack<String> ops = new Stack<String>();
Stack<Double> vals = new Stack<Double>();
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
if (s.Equals("(")){}
else if (s.Equals("+")) ops.Push(s);
else if (s.Equals("-")) ops.Push(s);
else if (s.Equals("*")) ops.Push(s);
else if (s.Equals("/")) ops.Push(s);
else if (s.Equals("sqrt")) ops.Push(s);
else if (s.Equals(")"))
{
int count = ops.Count;
while (count > 0)
{
String op = ops.Pop();
double v = vals.Pop();
if (op.Equals("+")) v = vals.Pop() + v;
else if (op.Equals("-")) v = vals.Pop() - v;
else if (op.Equals("*")) v = vals.Pop()*v;
else if (op.Equals("/")) v = vals.Pop()/v;
else if (op.Equals("sqrt")) v = Math.Sqrt(v);
vals.Push(v);
count--;
}
}
else vals.Push(Double.Parse(s));
}
return vals.Pop();
}
}
이것은 왼쪽에서 왼쪽에서 왼쪽으로 실행되므로 표현을 실행하기 위해 적절한 편기를 사용해야합니다.
// 2+(100/5)+10 = 32
//((2.5+10)/5)+2.5 = 5
// (2.5+10)/5+2.5 = 1.6666
public static double Evaluate(String expr)
{
Stack<String> stack = new Stack<String>();
string value = "";
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
char chr = s.ToCharArray()[0];
if (!char.IsDigit(chr) && chr != '.' && value != "")
{
stack.Push(value);
value = "";
}
if (s.Equals("(")) {
string innerExp = "";
i++; //Fetch Next Character
int bracketCount=0;
for (; i < expr.Length; i++)
{
s = expr.Substring(i, 1);
if (s.Equals("("))
bracketCount++;
if (s.Equals(")"))
if (bracketCount == 0)
break;
else
bracketCount--;
innerExp += s;
}
stack.Push(Evaluate(innerExp).ToString());
}
else if (s.Equals("+")) stack.Push(s);
else if (s.Equals("-")) stack.Push(s);
else if (s.Equals("*")) stack.Push(s);
else if (s.Equals("/")) stack.Push(s);
else if (s.Equals("sqrt")) stack.Push(s);
else if (s.Equals(")"))
{
}
else if (char.IsDigit(chr) || chr == '.')
{
value += s;
if (value.Split('.').Length > 2)
throw new Exception("Invalid decimal.");
if (i == (expr.Length - 1))
stack.Push(value);
}
else
throw new Exception("Invalid character.");
}
double result = 0;
while (stack.Count >= 3)
{
double right = Convert.ToDouble(stack.Pop());
string op = stack.Pop();
double left = Convert.ToDouble(stack.Pop());
if (op == "+") result = left + right;
else if (op == "+") result = left + right;
else if (op == "-") result = left - right;
else if (op == "*") result = left * right;
else if (op == "/") result = left / right;
stack.Push(result.ToString());
}
return Convert.ToDouble(stack.Pop());
}
적절한 보풀을 감싸는 Csharpcodeprovider를 통해 이것을 쉽게 실행할 수 있습니다 (기본적으로 유형 및 방법). 마찬가지로 다른 답변이 제안한대로 VB 등 또는 JavaScript를 살펴볼 수 있습니다. 나는이 시점에서 프레임 워크에 내장 된 다른 것을 모른다.
동적 언어를 지원하는 .NET 4.0 은이 전선에 더 나은 기능을 가질 수있을 것으로 기대합니다.
나는 최근에 프로젝트를 위해 이것을해야했고 결국 사용하게되었습니다. Ironpython 그것을하기 위해. 엔진 인스턴스를 선언 한 다음 유효한 파이썬 표현식을 전달하고 결과를 얻을 수 있습니다. 단순한 수학 표현을하고 있다면 충분할 것입니다. 내 코드는 다음과 비슷하게 보였습니다.
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);
각 표현식에 대한 엔진을 만들고 싶지 않을 것입니다. Ironpython.dll에 대한 참조도 필요합니다
편집하다: 나는 조금 더 많은 Bodmas를 준수하기 위해 추가 및 뺄셈을 별도로 가져와야한다는 것을 깨달았습니다.
스택 기반 접근 방식에 대한 Rajesh Jinaga에게 큰 감사를드립니다. 나는 그것이 내 필요에 정말로 유용하다는 것을 알았습니다. 다음 코드는 라 제시의 방법을 약간 수정하여 먼저 부서를 처리 한 다음 곱하기를 처리 한 다음 추가 및 뺄셈으로 마무리됩니다. 또한 표현에서 부울 논리를 사용하는 것을 허용하는 표현식에서 부울을 사용할 수 있습니다.
public static double Evaluate(string expr)
{
expr = expr.ToLower();
expr = expr.Replace(" ", "");
expr = expr.Replace("true", "1");
expr = expr.Replace("false", "0");
Stack<String> stack = new Stack<String>();
string value = "";
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
// pick up any doublelogical operators first.
if (i < expr.Length - 1)
{
String op = expr.Substring(i, 2);
if (op == "<=" || op == ">=" || op == "==")
{
stack.Push(value);
value = "";
stack.Push(op);
i++;
continue;
}
}
char chr = s.ToCharArray()[0];
if (!char.IsDigit(chr) && chr != '.' && value != "")
{
stack.Push(value);
value = "";
}
if (s.Equals("("))
{
string innerExp = "";
i++; //Fetch Next Character
int bracketCount = 0;
for (; i < expr.Length; i++)
{
s = expr.Substring(i, 1);
if (s.Equals("(")) bracketCount++;
if (s.Equals(")"))
{
if (bracketCount == 0) break;
bracketCount--;
}
innerExp += s;
}
stack.Push(Evaluate(innerExp).ToString());
}
else if (s.Equals("+") ||
s.Equals("-") ||
s.Equals("*") ||
s.Equals("/") ||
s.Equals("<") ||
s.Equals(">"))
{
stack.Push(s);
}
else if (char.IsDigit(chr) || chr == '.')
{
value += s;
if (value.Split('.').Length > 2)
throw new Exception("Invalid decimal.");
if (i == (expr.Length - 1))
stack.Push(value);
}
else
{
throw new Exception("Invalid character.");
}
}
double result = 0;
List<String> list = stack.ToList<String>();
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "/")
{
list[i] = (Convert.ToDouble(list[i - 1]) / Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "*")
{
list[i] = (Convert.ToDouble(list[i - 1]) * Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "+")
{
list[i] = (Convert.ToDouble(list[i - 1]) + Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "-")
{
list[i] = (Convert.ToDouble(list[i - 1]) - Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
stack.Clear();
for (int i = 0; i < list.Count; i++)
{
stack.Push(list[i]);
}
while (stack.Count >= 3)
{
double right = Convert.ToDouble(stack.Pop());
string op = stack.Pop();
double left = Convert.ToDouble(stack.Pop());
if (op == "<") result = (left < right) ? 1 : 0;
else if (op == ">") result = (left > right) ? 1 : 0;
else if (op == "<=") result = (left <= right) ? 1 : 0;
else if (op == ">=") result = (left >= right) ? 1 : 0;
else if (op == "==") result = (left == right) ? 1 : 0;
stack.Push(result.ToString());
}
return Convert.ToDouble(stack.Pop());
}
나는 그것을하는 더 깨끗한 방법이있을 가능성이 있다는 것을 알고 있습니다. 누군가가 유용하다고 생각할 경우 Id는 첫 번째 모습을 공유한다고 생각했습니다.
Ramesh에게 감사드립니다. 그의 간단한 코드 버전을 사용하여 문자열을 데이터베이스에서 꺼내서 코드에서 부울 작업을 수행하는 데 사용했습니다.
X는 1500 또는 2100과 같은 숫자입니다.
기능은 x> 1400 및 x <1600과 같은 저장된 평가입니다.
function = relation[0].Replace("and","&&").Replace("x",x);
DataTable f_dt = new DataTable();
var f_var = f_dt.Compute(function,"");
if (bool.Parse(f_var.ToString()) { do stuff }
없기. 외부 라이브러리를 사용하거나 자신의 파서를 작성해야합니다. 당신이 그렇게 할 시간이 있다면, 나는 자신의 파서를 매우 흥미로운 프로젝트이기 때문에 자신의 파서를 작성하는 것이 좋습니다. 그렇지 않으면 같은 것을 사용해야합니다 BCPARSER.
짧은 대답 : 나는 그렇게 생각하지 않습니다. C# .NET은 (바이트 코드로) 컴파일되며 내가 아는 한 런타임에 문자열을 평가할 수 없습니다. 그러나 jscript .net은 할 수 있습니다. 그러나 나는 여전히 파서와 스택 기반 평가자를 직접 코딩하는 것이 좋습니다.