Страницы

Поиск по вопросам

суббота, 11 января 2020 г.

Парсинг уравнения regex

#c_sharp #регулярные_выражения


Добрый вечер. Помогите подправить регулярное выражение. Необходимо распарсить часть
уравнения, примерно такого вида  : 5+10+2+-1. Вся проблема в числах с отрицательными
значениями. Вот выражение которое есть :
(?-?[\d.,]+)(?[*\-+/])(?-?[\d.,]+)

Приведенный выше пример разбирает без проблем. Но уравнение такого вида 5+10-2+-1
парсит неправильно. Собственно в месте где 10-2 считает, что (2) это -2. 
Подскажите, пожалуйста, что нужно изменить в выражении, что бы разбор проходил правильно?    


Ответы

Ответ 1



Вполне можно вычислять и регулярными выражениями. Можно даже соблюдать абсолютно все приоритеты выполнения операций. Как я понял автор вопроса хочет: Находить пары чисел разделенных оператором Находить значение выражения для этой пары чисел Подставлять вместо найденного текста новое число Применять пункт 1, пока все не упростится до одного числа Я приведу пример, как это сделать просто для четырех операторов, далее можно бесконечно усложнять регулярное выражение организуя поддержку скобок и других операторов. /^(?:(-?\d+)([*\/+-])(-?\d+)$|.*?(\d+)([*\/])(-?\d+)|.*?(\d+)([+-])(-?\d+))/ Тестировал выражение в JavaScipt, поэтому нет именованных групп, а они очень пригодятся для сложного регулярного выражения вместе с рекурсивными ссылками (все это есть в С#). Смысл выражения очень прост первый приоритет на замену отдаем двум числам с любыми знаками, разделенных любым оператором, если весь текст состоит только из этих двух чисел. (-?\d+)([*\/+-])(-?\d+)$ Второй приоритет имеют операторы деления и умножения .*?(\d+)([*\/])(-?\d+) И низший приоритет у сложения и вычитания .*?(\d+)([+-])(-?\d+) Приоритеты создаются путем привязки регулярного выражения к началу текста и порядка расположения альтернатив в регулярном выражении. В зависимости от того, какой приоритет сработал будут матчиться группы 1-3, 4-6 или 7-9. Нет разницы какие группы сматчились. Результат будет всегда одинаков: число1 оператор число2. Рассмотрим как будет пошагово преобразован текст 5+10-2+-1 по такому алгоритму: 5+10-2+-1 => 15-2+-1 15-2+-1 => 13+-1 13+-1 => 12 В результате замен будут появляться последовательности операторов вроде -+- и.т.п. В таком случае необходимо предварительно искать такие последовательности и менять их на один оператор. Внимание Регулярное выражение выше несет учебный характер. В сниппете ниже Вы найдете код вычисляющий с учетом скобок, упрощающий последовательности операторов, поэтому там совершенно другое регулярное выражение, но введя туда арифметическое выражение Вы вычислите его с помощью регулярных выражений ;) Не забудьте посмотреть консоль- туда выводится порядок упрощения исходного выражения. $(document).ready(function() { function defined() { for (var i = 0; i < arguments.length; i++) if (typeof(arguments[i]) !== "undefined") return arguments[i]; return arguments[0]; }; $("#run").click(function() { var replacer = function() { var num1 = defined(arguments[2], arguments[6], arguments[10]); var operator = defined(arguments[3], arguments[7], arguments[11]); var num2 = defined(arguments[4], arguments[8], arguments[12]); var before = defined(arguments[1], ""); var before2 = defined(arguments[5], arguments[9], ""); return before + before2 + eval(num1 + operator + num2); }; var clearOperators = function(text) { return text.replace(/[+-]{2,}/g, function(val) { return (val.replace(/\+/g, "").length % 2 == 0) ? "+" : "-"; }).replace(/([*\/])\++/g, "$1"); }; var text = $("#text").val(); var re = /^(.*\((?=[^\)]*\))|)(?:(-?\d+)([*\/+-])(-?\d+)(?=(?:$|\)))|(.*?)(\d+)([*\/])(-?\d+)|(.*?)(\d+)([+-])(-?\d+))/; var oldLength = -1; while (!/^-?\d+$/.test(text) && oldLength != text.length) { oldLength = text.length; text = clearOperators(text); text = text.replace(/\(([+-]?\d+)\)/g, "$1"); text = clearOperators(text); if (text.length != oldLength) console.log(text); text = text.replace(re, replacer); console.log(text); } $("#result").html(text); }); }); =


Ответ 2



Регулярные выражения непригодны для подобных задач (подробнее см. иерархию Хомского). Однако, РВ-грамматики уже позволяют разбор подобных выражений. Простой пример подобной грамматики: http://pegjs.org/online Реализации РВ-грамматик существуют для многих языков, включая C# (например, http://otac0n.com/Pegasus/).

Комментариев нет:

Отправить комментарий