Преобразование выражения из текстового поля в математическое выражение в программном коде

Возможный дубликат:
Мне нужна быстрая среда выполнения анализатор выражений

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


person Mo Darwish    schedule 25.10.2012    source источник
comment
См. stackoverflow.com/questions/tagged/equation+c%23. Кроме того, вы используете ASP.NET? Сильверлайт?   -  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));

РЕДАКТИРОВАТЬ: немного больше информации. Ознакомьтесь с документацией MSDN для свойства Expression класса System.Data.DataColumn. Материал по синтаксису выражений описывает список команд, которые вы можете использовать в дополнение к арифметическим операторам. (например, 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: Найдите существующий оценщик математических выражений
  • Вариант Б. Напишите свой собственный синтаксический анализатор и логику для вычисления результата.

Прежде чем вдаваться в некоторые подробности об этом, уместно подчеркнуть, что интерпретация произвольных математических выражений является нетривиальной задачей для любой грамматики выражений, кроме "игрушечных" грамматик, таких как эти, которые принимают только одно или два выражения. арифметические операции и не допускают скобок и т. д.

Понимая, что такая задача обманчиво тривиальна, и признавая, что, в конце концов, интерпретация арифметических выражений средней сложности является относительно повторяющейся необходимостью для различных приложений [следовательно, для которых должны быть доступны зрелые решения], вероятно, разумно попытаться сделать это. с "Вариантом А".
Поэтому я бы поддержал рекомендацию Джеда о готовом оценщике выражений, таком как NCalc.

Однако может быть полезно уделить время и понять различные концепции и методы, связанные с синтаксическим анализом и интерпретацией арифметических выражений, как если бы кто-то собирался создать собственную реализацию.

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

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

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

Для очень простых грамматик можно написать соответствующий лексер и синтаксический анализатор с нуля. Но рано или поздно грамматика может усложниться до такой степени, что написание этих модулей от руки станет привередливым, подверженным ошибкам и, возможно, что более важно, трудным для чтения. Отсюда и существование генераторов лексеров и парсеров которые представляют собой автономные программы, которые создают код лексеров и синтаксических анализаторов (на определенном языке программирования, таком как C, Java или C#) из списка правил (выраженных в синтаксисе, специфичном для генератора, хотя многие генераторы, как правило, используют похожий синтаксис, основанный на BNF).

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

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

  • некоторые ограничения в отношении допустимого синтаксиса выражений (либо разрешенная грамматика слишком ограничительна: вам также нужно сказать stddev(), либо слишком широка: вы не хотите, чтобы ваши пользователи использовали триггерные функции. С более зрелыми оценщиками будет некоторая форма функции конфигурации/расширения, которая позволит справиться с этой проблемой.
  • кривая обучения такого стороннего модуля. Надеюсь, многие из них должны быть относительно "подключи и работай".
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