Възможен дубликат:
Имам нужда от бързо изпълнение анализатор на изрази
Как да направя така, че когато някой въведе x*y^z в текстово поле на моята страница, да изчисли това уравнение в кода отзад и да получи резултата?
Възможен дубликат:
Имам нужда от бързо изпълнение анализатор на изрази
Как да направя така, че когато някой въведе x*y^z в текстово поле на моята страница, да изчисли това уравнение в кода отзад и да получи резултата?
.NET няма вградена функция за оценяване на произволни низове. Обаче .NET библиотека с отворен код, наречена NCalc го прави.
NCalc е оценител на математически изрази в .NET. NCalc може да анализира всеки израз и да оцени резултата, включително статични или динамични параметри и персонализирани функции.
Отговор от оператори като низове от потребител https://stackoverflow.com/users/1670022/matt-crouch, използвайки вградена .NET функционалност:
Ако всичко, от което се нуждаете, е проста аритметика, направете това.
DataTable temp = new DataTable();
Console.WriteLine(temp.Compute("15 / 3",string.Empty));
EDIT: малко повече информация. Вижте документацията на MSDN за свойството Expression на класа System.Data.DataColumn. Нещата в Expression Syntax очертават списък с команди, които можете да използвате в допълнение към аритметичните оператори. (напр. IIF, LEN и др.).
РЕДАКТИРАНЕ 2: За удобство можете да поставите това в малка функция като:
public string Eval(string expr)
{
var temp = new System.Data.DataTable();
string result = null;
try
{
result = $"{temp.Compute(expr, string.Empty)}";
}
catch (System.Data.EvaluateException ex)
{
if (ex.Message.ToLower().Contains("cannot find column"))
throw new System.Data.SyntaxErrorException($"Syntax error: Invalid expression: '{expr}'."
+ " Variables as operands are not supported.");
else
throw;
}
return result;
}
Така че можете да го използвате като:
Console.WriteLine(Eval("15 * (3 + 5) / (7 - 2)"));
дава очаквания резултат:
24
Забележете, че манипулаторът на грешки помага за обработката на изключения, причинени от използването на променливи, които не са разрешени тук. Пример: Eval("a")
- Вместо да връща "Cannot find column [a]"
, което няма много смисъл в този контекст (ние не го използваме в контекст на база данни), то връща "Syntax error: Invalid expression: 'a'. Variables as operands are not supported."
Стартирайте го на DotNetFiddle
Има два основни подхода към този проблем, всеки с някои вариации, както е илюстрирано в разнообразието от отговори.
Преди да навлезем в някои подробности за това, уместно е да подчертаем, че интерпретирането на произволни математически изрази не е тривиална задача за всяка граматика на израза, различна от граматиките на „играчки“, като тези, които приемат само една или две аритметични операции и не позволяват скоби и др.
Разбирайки, че подобна задача е измамно тривиална и признавайки, че в края на краищата интерпретирането на аритметични изрази със средна сложност е относително повтаряща се необходимост за различни приложения [следователно такава, за която трябва да има зрели решения], вероятно е разумно да се опитате и да се справите с "Опция A".
Затова бих подкрепил препоръката на Jed за готов оценител на изрази като NCalc.
Въпреки това може да е полезно да отделите време и да разберете различните понятия и методи, свързани с анализирането и интерпретирането на аритметични изрази, сякаш човек ще разработи своя собствена реализация.
Обикновено граматиката работи върху токени, които преди това са били извлечени от необработения входен текст. По същество в някакъв момент от процеса, някаква логика трябва да анализира входния низ, символ по знак, и да определи кои последователности от знаци вървят заедно. Например в израза 123 + 45 / 9.3
токените са целочислената стойност 123
, операторът plus
, целочислената стойност 45
, операторът division
и накрая реалната стойност 9.3
. Задачата за идентифициране на токените и свързването им с тип токен е работа на лексер< /em>. Лексерите могат да се изграждат върху граматика (граматика, чиито "токени" са единични знаци, за разлика от граматиката за анализатора на аритметични изрази, чиито токени са кратки низове, произведени от лексера.)
Между другото, граматиките се използват за дефиниране на много други неща извън аритметичните изрази. Компютърните езици следват [доста сложни] граматики, но е относително често да се въвеждат специфични за домейн езици, известни още като DSL, в подкрепа на различни функции на компютърни приложения.
За много прости граматики човек може да успее да напише съответния лексер и анализатор от нулата. Но рано или късно граматиките могат да се усложнят до такава степен, че писането на ръка на тези модули да стане придирчиво, податливо на грешки и може би по-важното е трудно за четене. Оттук и съществуването на Lexer и Parser генератори които са самостоятелни програми, които произвеждат кода на лексери и парсери (на определен език за програмиране като C, Java или C#) от списък с правила (изразени в синтаксис, специфичен за генератора, въпреки че много генератори са склонни да използват подобен синтаксис, базиран на BNF).
Когато се използва такъв генератор на лексни/аналогични анализатори, работата се извършва на няколко стъпки:
- първо се пише дефиниция на граматиката (на специфичния за генератора език/синтаксис)
- тази граматика се пуска през генератора.
- човек често повтаря горните две стъпки многократно, тъй като писането на граматика е взискателно упражнение: генераторът ще се оплаква от много възможни неясноти, които може да запише в граматиката.
- в крайна сметка генераторът създава изходен файл ( на желания целеви език, като C# и т.н.)
- този източник е включен в цялостния проект
- други изходни файлове в проекта могат да извикват функциите, изложени в изходните файлове, произведени от генератора и/или някои логика, съответстваща на различни модели, идентифицирани по време на синтактичния анализ, може лесно да бъде вградена в произведения от генератора код.
- след това проектът може да бъде изграден както обикновено, т.е. сякаш парсерът и лексерът са написани на ръка.
И това е всичко за високо 20 000 фута представяне на процеса на работа с формални граматики и генератори на код.
Списък с генератори на парсери (известни още като компилатори-компилатори) може да бъде намерен на този връзка. За проста работа в C# искам също да спомена Irony. Може да е много проницателно да разгледате тези сайтове, за да получите по-добро усещане за тези концепции, дори и без намерението да станете практикуващ в момента.
Както казах, искам да подчертая, че за това конкретно приложение, готовият аритметичен оценител вероятно е по-добрият подход. Основният недостатък на тези би бил
решен с тази библиотека http://www.codeproject.com/Articles/21137/Inside-the-Mathematical-Expressions-Evaluator
моят последен код
Calculator Cal = new Calculator();
txt_LambdaNoot.Text = (Cal.Evaluate(txt_C.Text) / fo).ToString();
сега, когато някой напише 3*10^11, той ще получи 300000000000
Ще трябва да внедрите (или да намерите източник на трета страна) анализатор на изрази. Това не е тривиално нещо.
Това, от което се нуждаете - ако искате да го направите сами - е Скенер (известен също като Lexer) + Анализатор в кода, зад който интерпретира израза. Като алтернатива можете да намерите библиотека на трета страна, която върши работата и работи подобно на функцията JavaScript eval(string)
.
Моля, погледнете тук, описва синтактичен анализатор с рекурсивно спускане. Примерът е написан на C, но трябва лесно да можете да го адаптирате към C#, след като получите идеята, описана в статията.
По-малко е сложно, отколкото звучи, особено ако имате ограничен брой оператори за поддръжка.
Предимството е, че запазвате пълен контрол върху това какви изрази ще бъдат изпълнени (за да предотвратите инжектиране на зловреден код от крайния потребител на вашия уебсайт).