Преобразуване на израз от текстово поле в математически израз в кода зад

Възможен дубликат:
Имам нужда от бързо изпълнение анализатор на изрази

Как да направя така, че когато някой въведе x*y^z в текстово поле на моята страница, да изчисли това уравнение в кода отзад и да получи резултата?


person Mo Darwish    schedule 25.10.2012    source източник
comment
Вижте stackoverflow.com/questions/tagged/equation+c%23. Също така, използвате ли ASP.NET? Silverlight?   -  person John Saunders    schedule 25.10.2012


Отговори (6)


.NET няма вградена функция за оценяване на произволни низове. Обаче .NET библиотека с отворен код, наречена NCalc го прави.

NCalc е оценител на математически изрази в .NET. NCalc може да анализира всеки израз и да оцени резултата, включително статични или динамични параметри и персонализирани функции.

person Jed    schedule 25.10.2012
comment
“ изненадах се, че предложението ви не се просмука лесно в горната част. По-добре да направя моята част (+1), тъй като включвам този подход в собствения си манифест - person mjv; 25.10.2012
comment
Забележка: NCalc е преместен в Github: github.com/sheetsync/NCalc и няма да се поддържа повече. Връзката (архив) в отговора предлага да разгледате github.com/sebastienros/jint, което е продължено да бъдат разработени. - person Matt; 15.07.2021

Отговор от оператори като низове от потребител 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

person Greg    schedule 25.10.2012

Има два основни подхода към този проблем, всеки с някои вариации, както е илюстрирано в разнообразието от отговори.

  • Вариант A: Намерете съществуващ оценител на математически изрази
  • Вариант Б: Напишете свой собствен анализатор и логиката за изчисляване на резултата

Преди да навлезем в някои подробности за това, уместно е да подчертаем, че интерпретирането на произволни математически изрази не е тривиална задача за всяка граматика на израза, различна от граматиките на „играчки“, като тези, които приемат само една или две аритметични операции и не позволяват скоби и др.

Разбирайки, че подобна задача е измамно тривиална и признавайки, че в края на краищата интерпретирането на аритметични изрази със средна сложност е относително повтаряща се необходимост за различни приложения [следователно такава, за която трябва да има зрели решения], вероятно е разумно да се опитате и да се справите с "Опция A".
Затова бих подкрепил препоръката на Jed за готов оценител на изрази като NCalc.

Въпреки това може да е полезно да отделите време и да разберете различните понятия и методи, свързани с анализирането и интерпретирането на аритметични изрази, сякаш човек ще разработи своя собствена реализация.

Ключовата концепция е тази на официална граматика. Аритметичните изрази, които оценителят ще приеме, трябва да следват набор от правила като списъка с разрешени аритметични операции. Например ще поддържа ли оценителят, да кажем, тригонометрични функции, или ако поддържа, това ще включва ли също така да кажем atan2(). Правилата също така посочват какво представлява операнд, например ще бъде ли позволено да се въвеждат числови стойности, големи като например 45 цифри. и т.н. Въпросът е, че всички тези правила са формализирани в граматика.

Обикновено граматиката работи върху токени, които преди това са били извлечени от необработения входен текст. По същество в някакъв момент от процеса, някаква логика трябва да анализира входния низ, символ по знак, и да определи кои последователности от знаци вървят заедно. Например в израза 123 + 45 / 9.3 токените са целочислената стойност 123, операторът plus, целочислената стойност 45, операторът division и накрая реалната стойност 9.3. Задачата за идентифициране на токените и свързването им с тип токен е работа на лексер< /em>. Лексерите могат да се изграждат върху граматика (граматика, чиито "токени" са единични знаци, за разлика от граматиката за анализатора на аритметични изрази, чиито токени са кратки низове, произведени от лексера.)

Между другото, граматиките се използват за дефиниране на много други неща извън аритметичните изрази. Компютърните езици следват [доста сложни] граматики, но е относително често да се въвеждат специфични за домейн езици, известни още като DSL, в подкрепа на различни функции на компютърни приложения.

За много прости граматики човек може да успее да напише съответния лексер и анализатор от нулата. Но рано или късно граматиките могат да се усложнят до такава степен, че писането на ръка на тези модули да стане придирчиво, податливо на грешки и може би по-важното е трудно за четене. Оттук и съществуването на Lexer и Parser генератори които са самостоятелни програми, които произвеждат кода на лексери и парсери (на определен език за програмиране като C, Java или C#) от списък с правила (изразени в синтаксис, специфичен за генератора, въпреки че много генератори са склонни да използват подобен синтаксис, базиран на BNF).

Когато се използва такъв генератор на лексни/аналогични анализатори, работата се извършва на няколко стъпки:
- първо се пише дефиниция на граматиката (на специфичния за генератора език/синтаксис)
- тази граматика се пуска през генератора.
- човек често повтаря горните две стъпки многократно, тъй като писането на граматика е взискателно упражнение: генераторът ще се оплаква от много възможни неясноти, които може да запише в граматиката.
- в крайна сметка генераторът създава изходен файл ( на желания целеви език, като C# и т.н.)
- този източник е включен в цялостния проект
- други изходни файлове в проекта могат да извикват функциите, изложени в изходните файлове, произведени от генератора и/или някои логика, съответстваща на различни модели, идентифицирани по време на синтактичния анализ, може лесно да бъде вградена в произведения от генератора код.
- след това проектът може да бъде изграден както обикновено, т.е. сякаш парсерът и лексерът са написани на ръка.

И това е всичко за високо 20 000 фута представяне на процеса на работа с формални граматики и генератори на код.
Списък с генератори на парсери (известни още като компилатори-компилатори) може да бъде намерен на този връзка. За проста работа в C# искам също да спомена Irony. Може да е много проницателно да разгледате тези сайтове, за да получите по-добро усещане за тези концепции, дори и без намерението да станете практикуващ в момента.
Както казах, искам да подчертая, че за това конкретно приложение, готовият аритметичен оценител вероятно е по-добрият подход. Основният недостатък на тези би бил

  • някои ограничения по отношение на това какъв е разрешеният синтаксис на израз (или разрешената граматика е твърде ограничителна: трябва също да кажете stddev() или е твърде широка: не искате вашите потребители да използват тригонометрични функции. С по-зрелите оценители има ще бъде някаква форма на функция за конфигурация/разширение, която позволява справяне с този проблем.
  • кривата на обучение на такъв модул на трета страна. Да се ​​надяваме, че много от тях трябва да бъдат сравнително „plug-and-play“.
person mjv    schedule 25.10.2012

решен с тази библиотека 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

person Mo Darwish    schedule 27.10.2012

Ще трябва да внедрите (или да намерите източник на трета страна) анализатор на изрази. Това не е тривиално нещо.

person David R Tribble    schedule 25.10.2012

Това, от което се нуждаете - ако искате да го направите сами - е Скенер (известен също като Lexer) + Анализатор в кода, зад който интерпретира израза. Като алтернатива можете да намерите библиотека на трета страна, която върши работата и работи подобно на функцията JavaScript eval(string).

Моля, погледнете тук, описва синтактичен анализатор с рекурсивно спускане. Примерът е написан на C, но трябва лесно да можете да го адаптирате към C#, след като получите идеята, описана в статията.

По-малко е сложно, отколкото звучи, особено ако имате ограничен брой оператори за поддръжка.

Предимството е, че запазвате пълен контрол върху това какви изрази ще бъдат изпълнени (за да предотвратите инжектиране на зловреден код от крайния потребител на вашия уебсайт).

person Matt    schedule 25.10.2012