¿Coincidencia de la expresión matemática con la expresión regular?

Por ejemplo, estas son expresiones matemáticas válidas:

a * b + c -a * (b / 1.50) (apple + (-0.5)) * (boy - 1) 

Y estas son expresiones matemáticas no válidas:

 --a *+ b @ 1.5.0 // two consecutive signs, two consecutive operators, invalid operator, invalid number -a * b + 1) // unmatched parentheses a) * (b + c) / (d // unmatched parentheses 

No tengo problemas para hacer coincidir los números de flotación, pero tengo dificultades con la coincidencia de paréntesis. ¿Alguna idea? Si hay una solución mejor que la expresión regular, lo aceptaré también. Pero se prefiere la expresión regular.

========

Editar:

Quiero hacer algunos comentarios sobre mi elección de la “respuesta aceptada”, con la esperanza de que las personas que tengan la misma pregunta y encuentren este hilo no sean engañadas.

Hay varias respuestas que considero “aceptadas”, pero no tengo idea de cuál es la mejor. Así que elegí la respuesta aceptada (casi) al azar. Recomiendo leer la respuesta de Guillaume Malartre además de la respuesta aceptada. Todos ellos dan soluciones prácticas a mi pregunta. Para una respuesta algo rigurosa / teórica, lea los comentarios de David Thornley en la respuesta aceptada. Como él mencionó, la extensión de Perl a la expresión regular (originada del lenguaje regular) la hace “irregular”. (No mencioné ningún idioma en mi pregunta, por lo que la mayoría de quienes respondieron asumieron la implementación de expresiones regulares de Perl, probablemente la implementación más popular. Lo mismo hice cuando publiqué mi pregunta).

Por favor corrígeme si dije algo incorrecto arriba.

Coincidencia de parens con una expresión regular es bastante posible.

Aquí hay una secuencia de comandos de Perl que analizará paréntesis arbitrarios profundos. Si bien arrojará fuera parens parens fuera, no lo diseñé específicamente para validar parens. Analizará parens arbitrariamente profundos siempre que estén equilibrados. Esto te ayudará a comenzar sin embargo.

La clave es la recursión tanto en la expresión regular como en su uso. Juega con él, y estoy seguro de que puedes conseguir que también marque prens no coincidentes. Creo que si capturas lo que esta expresión regular descarta y cuenta los paréntesis (es decir, prueba los paréntesis impares en el texto que no coincide), tienes paréntesis no desequilibrados y no válidos.

 #!/usr/bin/perl $re = qr / ( # start capture buffer 1 \( # match an opening paren ( # capture buffer 2 (?: # match one of: (?> # don't backtrack over the inside of this group [^()]+ # one or more ) # end non backtracking group | # ... or ... (?1) # recurse to opening 1 and try it again )* # 0 or more times. ) # end of buffer 2 \) # match a closing paren ) # end capture buffer one /x; sub strip { my ($str) = @_; while ($str=~/$re/g) { $match=$1; $striped=$2; print "$match\n"; strip($striped) if $striped=~/\(/; return $striped; } } while() { print "start pattern: $_"; while (/$re/g) { strip($1) ; } } __DATA__ "(apple + (-0.5)) * (boy - 1)" "((((one)two)three)four)x(one(two(three(four))))" "a) * (b + c) / (d" "-a * (b / 1.50)" 

Salida:

 start pattern: "(apple + (-0.5)) * (boy - 1)" (apple + (-0.5)) (-0.5) (boy - 1) start pattern: "((((one)two)three)four)x(one(two(three(four))))" ((((one)two)three)four) (((one)two)three) ((one)two) (one) (one(two(three(four)))) (two(three(four))) (three(four)) (four) start pattern: "a) * (b + c) / (d" (b + c) start pattern: "-a * (b / 1.50)" (b / 1.50) 

Use un autómata de empuje para hacer coincidir la paranthesis http://en.wikipedia.org/wiki/Pushdown_automaton (o solo una stack ;-))

Detalles para la solución de stack:

 while (chr available) if chr == '(' then push '(' else if chr == ')' then if stack.elements == 0 then print('too many or misplaced )') exit else pop //from stack end while if (stack.elements != 0) print('too many or misplaced(') 

Incluso simple: solo mantén un contador en lugar de astackr.

Las expresiones regulares solo se pueden usar para reconocer idiomas regulares. El lenguaje de las expresiones matemáticas no es regular; necesitarás implementar un analizador real (por ejemplo, LR) para poder hacer esto.

Creo que será mejor que implemente un analizador real para lograr lo que busca.

Un analizador sintáctico para expresiones matemáticas simples es “Parsing 101”, y hay varios ejemplos que se pueden encontrar en línea.

Algunos ejemplos incluyen:

Tenga en cuenta que la gramática que necesitará para validar expresiones es más simple que los ejemplos anteriores, ya que los ejemplos también implementan la evaluación de la expresión.

No puede usar expresiones regulares para hacer cosas como equilibrar paréntesis.

Esto es complicado con una sola expresión regular, pero es bastante fácil utilizar un enfoque mixto de expresiones regulares / procedimiento. La idea es construir una expresión regular para la expresión simple (sin paréntesis) y luego reemplazar repetidamente ( simple-expression ) con alguna cadena atómica (por ejemplo, identificador). Si la expresión reducida final coincide con el mismo patrón “simple”, la expresión original se considera válida.

Ilustración (en php).

 function check_syntax($str) { // define the grammar $number = "\d+(\.\d+)?"; $ident = "[az]\w*"; $atom = "[+-]?($number|$ident)"; $op = "[+*/-]"; $sexpr = "$atom($op$atom)*"; // simple expression // step1. remove whitespace $str = preg_replace('~\s+~', '', $str); // step2. repeatedly replace parenthetic expressions with 'x' $par = "~\($sexpr\)~"; while(preg_match($par, $str)) $str = preg_replace($par, 'x', $str); // step3. no more parens, the string must be simple expression return preg_match("~^$sexpr$~", $str); } $tests = array( "a * b + c", "-a * (b / 1.50)", "(apple + (-0.5)) * (boy - 1)", "--a *+ b @ 1.5.0", "-a * b + 1)", "a) * (b + c) / (d", ); foreach($tests as $t) echo $t, "=", check_syntax($t) ? "ok" : "nope", "\n"; 

Lo anterior solo valida la syntax, pero la misma técnica también se puede usar para construir un analizador real.

Para emparejar paréntesis e implementar otras reglas de validación de expresiones, probablemente sea más fácil escribir su propio pequeño analizador. Las expresiones regulares no son buenas en este tipo de situación.

Ok aquí está mi versión de búsqueda de paréntesis en ActionScript3, usando este enfoque da mucha tracción para analizar la parte antes del paréntesis, dentro del paréntesis y después de parenthis, si algún paréntesis permanece al final puedes levantar una advertencia o rechazar enviar a una función de evaluación final.

 package { import flash.display.Sprite; import mx.utils.StringUtil; public class Stackoverflow_As3RegexpExample extends Sprite { private var tokenChain:String = "2+(3-4*(4/6))-9(82+-21)" //Constructor public function Stackoverflow_As3RegexpExample() { // remove the "\" that just escape the following "\" if you want to test outside of flash compiler. var getGroup:RegExp = new RegExp("((?:[^\\(\\)]+)?) (?:\\() ( (?:[^\\(\\)]+)? ) (?:\\)) ((?:[^\\(\\)]+)?)", "ix") //removed g flag while (true) { tokenChain = replace(tokenChain,getGroup) if (tokenChain.search(getGroup) == -1) break; } trace("cummulativeEvaluable="+cummulativeEvaluable) } private var cummulativeEvaluable:Array = new Array() protected function analyseGrammar(matchedSubstring:String, capturedMatch1:String, capturedMatch2:String, capturedMatch3:String, index:int, str:String):String { trace("\nanalyseGrammar str:\t\t\t\t'"+str+"'") trace("analyseGrammar matchedSubstring:'"+matchedSubstring+"'") trace("analyseGrammar capturedMatchs:\t'"+capturedMatch1+"' '("+capturedMatch2+")' '"+capturedMatch3+"'") trace("analyseGrammar index:\t\t\t'"+index+"'") var blank:String = buildBlank(matchedSubstring.length) cummulativeEvaluable.push(StringUtil.trim(matchedSubstring)) // I could do soo much rigth here! return str.substr(0,index)+blank+str.substr(index+matchedSubstring.length,str.length-1) } private function replace(str:String,regExp:RegExp):String { var result:Object = regExp.exec(str) if (result) return analyseGrammar.apply(null,objectToArray(result)) return str } private function objectToArray(value:Object):Array { var array:Array = new Array() var i:int = 0 while (true) { if (value.hasOwnProperty(i.toString())) { array.push(value[i]) } else { break; } i++ } array.push(value.index) array.push(value.input) return array } protected function buildBlank(length:uint):String { var blank:String = "" while (blank.length != length) blank = blank+" " return blank } } 

}

Debe rastrear esto:

 analyseGrammar str: '2+(3-4*(4/6))-9(82+-21)' analyseGrammar matchedSubstring:'3-4*(4/6)' analyseGrammar capturedMatchs: '3-4*' '(4/6)' '' analyseGrammar index: '3' analyseGrammar str: '2+( )-9(82+-21)' analyseGrammar matchedSubstring:'2+( )-9' analyseGrammar capturedMatchs: '2+' '( )' '-9' analyseGrammar index: '0' analyseGrammar str: ' (82+-21)' analyseGrammar matchedSubstring:' (82+-21)' analyseGrammar capturedMatchs: ' ' '(82+-21)' '' analyseGrammar index: '0' cummulativeEvaluable=3-4*(4/6),2+( )-9,(82+-21)