Регулярные выражения и математический парсер

от автора

Когда-то давно, мне понадобился парсер математических выражений на C#. Конечно, скачать готовую реализацию — не проблема. Но вот только Интернета у меня в те годы не было. В итоге абсолютно без раздумий и без теоретических основ парсеров, конечных автоматов и прочего он был написан через регулярные выражения. Минут за 10. Стоит отметить, что нужны были только арифметический действия и скобки. Поддержка тригонометрических функций и прочего не требовалась.

Для начала выделим регулярные выражения для чисел и действий:

private const string RegexBr = "\\(([1234567890\\.\\+\\-\\*\\/^%]*)\\)";    // Скобки private const string RegexNum = "[-]?\\d+\\.?\\d*";                         // Числа private const string RegexMulOp = "[\\*\\/^%]";                             // Первоприоритетные числа private const string RegexAddOp = "[\\+\\-]";                               // Второприорететные числа 

Теперь метод, который полученную строку разделяет на элементы и рекурсивно их вычисляет:

public static double Parse(string str) {     // Парсинг скобок     var matchSk = Regex.Match(str, RegexBr);     if (matchSk.Groups.Count > 1)     {         string inner = matchSk.Groups[0].Value.Substring(1, matchSk.Groups[0].Value.Trim().Length - 2);         string left = str.Substring(0, matchSk.Index);         string right = str.Substring(matchSk.Index + matchSk.Length);          return Parse(left + Parse(inner).ToString(CultureInfo.InvariantCulture) + right);     }      // Парсинг действий     var matchMulOp = Regex.Match(str, string.Format("({0})\\s?({1})\\s?({2})\\s?", RegexNum, RegexMulOp, RegexNum));     var matchAddOp = Regex.Match(str, string.Format("({0})\\s?({1})\\s?({2})\\s?", RegexNum, RegexAddOp, RegexNum));     var match = matchMulOp.Groups.Count > 1 ? matchMulOp : matchAddOp.Groups.Count > 1 ? matchAddOp : null;     if (match != null)     {         string left = str.Substring(0, match.Index);         string right = str.Substring(match.Index + match.Length);         return Parse(left + ParseAct(match).ToString(CultureInfo.InvariantCulture) + right);     }      // Парсинг числа     try     {         return double.Parse(str, CultureInfo.InvariantCulture);     }     catch (FormatException)     {         throw new FormatException(string.Format("Неверная входная строка '{0}'", str));     } } 

И последним напишем метод, который непосредственно вычисляет значение действия:

private static double ParseAct(Match match) {     double a = double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);     double b = double.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture);      switch (match.Groups[2].Value)     {         case "+": return a + b;         case "-": return a - b;         case "*": return a * b;         case "/": return a / b;         case "^": return Math.Pow(a, b);         case "%": return a % b;         default: throw new FormatException($"Неверная входная строка '{match.Value}'");     } } 

Такое вот «ненормальное программирование» у меня было. Исходный текст полностью приводить не вижу никакого смысла — вряд ли это кому-нибудь пондобится. Но в любом случае составить класс из трех кусков — дело пары секунд. Спасибо за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/271285/