• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[FRAGE] match regex

TecEye

New member
hy leute, ich verzweifel mit diesem regex-mist :(
hab einen string ala
Code:
{CiuZQ7FDXD}-({00RyWNRMT1}*(60*60*24))

ich benötige die werte aus den geschweiften klammern im array, habe bislang das
Code:
var str_berechnung	= $(this).attr("data-berechnung");
console.log("Berechnung: "+str_berechnung);
var variables	= str_berechnung.match(/[^{\}]+(?=})/i);
console.log("Variablen: "+variables);

bekomme aber nur den ersten Wert CiuZQ7FDXD.

irgendwas ist falsch an meinem Pattern: /[^{\}]+(?=})/i
Jemand einen Tipp? Danke :)
 
Zuletzt bearbeitet von einem Moderator:
muss mich echt mal viel ausgiebiger mit regex beschäftigen
Ich arbeite mit Regex nahezu ausnahmslos nur serverseitig in Perl, nicht im Browser. In JS gefällt mir die Syntax nicht und die meisten Prüfungen muss man ja serverseitig eh durchführen, daher beschränken sich meine Eingabeprüfungen auf die HTML 5 Feldattribute die man benutzen kann.
 
leider tuts der pattern mit anderem ausgangs_string nicht:
Code:
({00RyWNRMT1}+{aK37rafH5w}) / 2

- - - Aktualisiert - - -

Ich arbeite mit Regex nahezu ausnahmslos nur serverseitig in Perl, nicht im Browser. In JS gefällt mir die Syntax nicht und die meisten Prüfungen muss man ja serverseitig eh durchführen, daher beschränken sich meine Eingabeprüfungen auf die HTML 5 Feldattribute die man benutzen kann.

ich arbeite gerade an einer methode um mit Feldern zu rechnen. die Felder haben Bezeichnungen ala data-shortcode="CiuZQ7FDXD" und es gibt Felder die liefern Ergebnisse aus ala data-chiffre="({00RyWNRMT1}+{aK37rafH5w}) / 2"

Leider muss ich hier clientseitig arbeiten, da ich auf die direkten Eingaben reagieren muss (klar könnte ich das auch via ajax mit php verarbeiten, aber wozu die lange Leitung wenn es auch so geht)
eine schleife sucht also alle "Ergebnisfelder" holt die Chiffre, parst dann die einzelnen shortcodes, sucht die inputs mit den werten, rechnet aus und liefert das Ergebnis zurück ;) So der Plan :D
 

astrein :D DANKE!!!!!!

- - - Aktualisiert - - -

wenn ich alle werte beisammen habe kommt eine berechnung als string raus, gibt es eine elegantere/sicherere lösung als evil eval? soll mir ja keiner schadsoftware unterjubeln :D

- - - Aktualisiert - - -

SORRY bei einem anderen String geht es wieder nicht
Code:
({CiuZQ7FDXD}-({00RyWNRMT1}*(60*60*24))) * {34RTWNRMT5}
Die Anzahl und Position der Shortcodes ist variabel :/
Acht die Opperatoren oder art der Berechnung kann auch variieren zB
Code:
({CiuZQ7FDXD}-(date({00RyWNRMT1}))) * num(2,{34RTWNRMT5})
 
Zuletzt bearbeitet:
kann man sagen, dass alles in geschweiften klammern benötigt wird oder ist das zuviel? langsam wird das nämlich für einen einzelnen regex zu fehleranfällig.
 
Ja brauche alles was in geschweiften klammern steht, das ist eine Konstante

- - - Aktualisiert - - -

das ganze kann sogar bis zu 20 shortcodes haben, wie ich den regex so verstehe fragst du nach jedem einzelenen ab ne? das ist zu kompliziert
 
Ich glaube nicht, dass du das sauber mit RegExp gelöst bekommst. Bau' dir doch einen Parser - da kannst du dann auch gleich einbauen, dass du auf korrekte Formellogik prüfst.
 
Wieso steht der Beitrag auf gelöst?
TecEye schrieb:
ich benötige die werte aus den geschweiften klammern im array, habe bislang das
Würde ich jetzt so machen:
Code:
		function run(){
				let str = "{CiuZQ7FDXD}-({00RyWNRMT1}*(60*60*24))";//({CiuZQ7FDXD}-({00RyWNRMT1}*(60*60*24))) * {34RTWNRMT5}
				let a = str.match(/\{(.*?)\}/g);
				let res = [];
				for(let i=0;i<a.length;i++){
					res.push(a[i].substring(1,a[i].length-1));//schweif klammern entfernen
				}
				console.log(res);
		}

TecEye schrieb:
wenn ich alle werte beisammen habe kommt eine berechnung als string raus, gibt es eine elegantere/sicherere lösung als evil eval?
Wie genau meinst du Das? Du meinst den String ausführen und die Werte berechnen.?

Naja du kannst das mal Probieren. Ob das nun jeder Browser versteht ist das andere:
Code:
var theInstructions = "return 2+2";// return ist wichtig! 
var F=new Function (theInstructions);
var result = F();
console.log(result);
 
Zuletzt bearbeitet:
was meinst du mit Parser? finde dazu nichts aussagekräftiges bei Google :/
damit ist gemeint, den string schrittweise zu zerlegen. in deinem fall würde man beispielsweise erstmal hergehen und per iteration über den string erstmal alles raus ziehen was zwischen geschweiften klammern steht. im zweiten schritt würde man dann ineinander verschachtelte klammern auseinander ziehen, im dritten die klammerinhalte extrahieren und im vierten unerwünschte zeichen und dubletten entfernen.
das ist nur ein beispiel!
teilweise ist es in xorg's antwort sogar so gemacht.

Wieso steht der Beitrag auf gelöst?
weil er das schon war und nicht absehbar war, dass jetzt noch eine halbe dissertation entsteht. :confused:
 
In diesem Fall sollte ein Parser nicht iterativ arbeiten, sondern müsste eigentlich mit einem Durchgang auskommen:
Code:
<!DOCTYPE html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<script type="text/javascript">
class StringIterator{
	constructor(str){
		this.position = 0;
		this.string = str;
		this.advance(0);
	}
	next(){
		this.advance(1);
	}
	advance(length){
		this.position += length;
		var whitespace = this.rest().match(/^\s*/);
		this.position += whitespace[0].length;
	}
	current(length){
		return this.string.substr(this.position, length || 1);
	}
	rest(){
		return this.current(this.string.length);
	}
	goToNext(character){
		var pos = this.string.indexOf(character, this.position);
		var ret = this.string.substring(this.position, pos);
		this.position = pos;
		return ret;
	}
	valid(){
		return this.position < this.string.length;
	}
};

class Variable{
	constructor(obj, value){
		if (
			obj instanceof HTMLInputElement ||
			obj instanceof HTMLSelectElement ||
			obj instanceof HTMLTextareaElement
		){
			this.getValue = function(){return parseFloat(obj.value);};
			this.getName = function(){return obj.name || obj.id;};
		}
		else {
			this.getValue = function(){return parseFloat(value);};
			this.getName = function(){return obj;};
		}
	}
}

class Node{
	constructor(parser){
		this.parser = parser;
	}
}

class VariableNode extends Node{
	parse(iterator){
		iterator.next();
		this.variableName = iterator.goToNext("}");
		iterator.next();
	}
	evaluate(){
		return this.parser.getVariable(this.variableName).getValue();
	}
	static test(iterator){
		return iterator.current() === "{";
	}
};

class NumberNode extends Node{
	parse(iterator){
		var rest = iterator.rest();
		var match = rest.match(/^-?\d+(?:\.\d+)?(?:e[+\-]?\d+)?/i);
		this.value = parseFloat(match[0]);
		iterator.advance(match[0].length);
	}
	evaluate(){
		return this.value;
	}
	static test(iterator){
		return iterator.current(2).match(/^-?\d/);
	}
};

class OperatorNode extends Node{
	parse(iterator){
		this.operator = iterator.current();
		iterator.next();
	}
	getPrecendence(){
		return {
			"+": 2,
			"-": 2,
			"*": 1,
			"/": 1
		}[this.operator];
	}
	evaluate(op1, op2){
		// console.log(op1, this.operator, op2);
		switch (this.operator){
			case "+": return op1 + op2;
			case "-": return op1 - op2;
			case "*": return op1 * op2;
			case "/": return op1 / op2;
		}
	}
	static test(iterator){
		return iterator.current().match(/[+\-*\/]/);
	}
};

class Bracket extends Node{
	parse(iterator){
		iterator.next();
		var valueNodeClasses = [Bracket, NumberNode, VariableNode];
		var operatorNodeClasses = [OperatorNode];
		
		function callTest(nodeClass){
			return nodeClass.test(iterator);
		}
		
		var firstValue = valueNodeClasses.filter(callTest)[0];
		if (!firstValue){
			throw new Error("Expected value at position " + iterator.position);
		}
		firstValue = new firstValue(this.parser);
		firstValue.parse(iterator);
		
		this.chain = [firstValue];
		
		while (iterator.valid() && iterator.current() !== ")"){
			var operator = operatorNodeClasses.filter(callTest)[0];
			if (!operator){
				throw new Error("Expected operator at position " + iterator.position);
			}
			operator = new operator(this.parser);
			operator.parse(iterator);
			
			var value = valueNodeClasses.filter(callTest)[0];
			if (!value){
				throw new Error("Expected value at position " + iterator.position);
			}
			value = new value(this.parser);
			value.parse(iterator);
			
			this.chain.push(operator);
			this.chain.push(value);
		}
		
		if (!iterator.valid()){
			throw new Error("Unexpected end.");
		}
		
		while (this.chain.length > 3){
			var minPred = Number.POSITIVE_INFINITY;
			var minIndex = -1;
			for (var i = 1; i < this.chain.length; i += 2){
				if (this.chain[i].getPrecendence() < minPred){
					minPred = this.chain[i].getPrecendence();
					minIndex = i;
				};
			}
			var helpBracket = new Bracket(this.parser);
			helpBracket.chain = this.chain.splice(minIndex - 1, 3, helpBracket);
		}
		
		iterator.next();
	}
	evaluate(){
		if (this.chain.length === 1){
			return this.chain[0].evaluate();
		}
		else {
			return this.chain[1].evaluate(this.chain[0].evaluate(), this.chain[2].evaluate());
		}
	}
	static test(iterator){
		return iterator.current() === "(";
	}
};

class Parser {
	constructor(variables){
		this.variables = variables.map(function(variable){return new Variable(variable);});
	}
	parse(code){
		code = "(" + code + ")";
		var root = new Bracket(this);
		var iterator = new StringIterator(code);
		root.parse(iterator);
		if (iterator.valid()){
			throw new Error("Missmatching brackets.");
		}
		return root;
	}
	getVariable(name){
		return this.variables.filter(function(v){return v.getName() === name;})[0];
	}
}
</script>
<style type="text/css"></style>
</head>
<body>
Variable a: <input id="a" value="3"><br>
Variable b: <input id="b" value="7"><br>
Variable c: <input id="c" value="95"><br>
Formula: <textarea id="code">3 * ({a} + {c}) / {b}</textarea><br>
Result: <output id="result">
<script type="text/javascript">
(function(){
	var parser = new Parser(Array.from(document.querySelectorAll("input")));
	var code = parser.parse(document.getElementById("code").value);
	document.getElementById("result").value = code.evaluate();
	
	document.getElementById("code").addEventListener("input", function(){
		try {
			code = parser.parse(this.value);
			this.style.backgroundColor = "";
			this.title = "";
			document.getElementById("result").value = code.evaluate();
		}
		catch (e){
			this.style.backgroundColor = "lightpink";
			this.title = e.message;
		}
	});
	document.querySelectorAll("input").forEach(function(input){
		input.addEventListener("input", function(){
			document.getElementById("result").value = code.evaluate();
		});
	});
}());

</script>
</body>
</html>
 
das was du hier machen willst ist ein typischer fall für scanner/parser.
keine ahnung ob du dich schon mal mit lex/yacc (flex/bison von gnu) beschäftigt hast, aber eine ähnliche variante dieser bekannten scanner-/parsergeneratoren gibt es auch für js. jison
 
keine ahnung ob du dich schon mal mit lex/yacc (flex/bison von gnu) beschäftigt hast
anscheinend nicht
was meinst du mit Parser? finde dazu nichts aussagekräftiges bei Google :/

aber ich würde dir trotzdem raten das nicht mit regex zu lösen, das bekommst du nie unter kontrolle.
wenn du dir das calculator beispiel hier ansiehst, ist das schon fast 90% deiner gesuchten lösung.
die syntax ist nicht schwer zu verstehen. alles zw. %lex und /lex ist für den scanner der rest ist der parser
im scanner legst du die token fest die der parser in seinen regeln verwenden darf.
die token selbst werden wieder über regex definiert, kompliziertere sachen benötigst du vermutlich nicht.
der grundsätzliche aufbau des parser teils sieht so aus: Bison 1.875
im calculator-bsp kommen als erstes die beiden Bison Declarations Operator Precedence und Start-Symbol
dann kommen die Grammar rules. die sind im beispiel doch ganz übersichtlich. die regeln selbst sind eine form von bnf wie man sie z.b. aus der js-spec in ähnlicher weise kennt.
in geschweiften klammern {} stehen unter den regeln die Actions das ist js-code der ausgeführt wird, wenn eine regel matched.
$$ ist in den actions sozusagen der returnwert, $1 bis $n sind die werte welche hinter den teilregeln stehen welche die regel definieren und yytext ist der value welcher hinter einem token steht.
im beispiel:
Code:
e
    : e '+' e
        {$$ = $1+$3;}
    | e '-' e
        {$$ = $1-$3;}
    | e '*' e
        {$$ = $1*$3;}
    | e '/' e
        {$$ = $1/$3;}
    | e '^' e
        {$$ = Math.pow($1, $3);}
    | '-' e %prec UMINUS
        {$$ = -$2;}
    | '(' e ')'
        {$$ = $2;}
    | NUMBER
        {$$ = Number(yytext);}
    | E
        {$$ = Math.E;}
    | PI
        {$$ = Math.PI;}
    ;
die regel e besteht z.b. aus PI. PI ist im scanner als token definiert worden welcher aus dem string "PI" besteht. e wird dann der wert Math.PI in der action zugewiesen(Math.PI wird auf den stack gelegt)
mit E ist es das gleiche.
e kann aber auch aus dem token NUMBER bestehen. NUMBER wurde im scanner definiert als regex [0-9]+("."[0-9]+)?\b. 123 wird vom scanner also als NUMBER identifiziert und dem parser übergeben.
die action hinter NUMBER weist e wieder den wert zu. diesmal ist das token aber keine konstante, sondern variabel. der string "123" ,welcher vom scanner also als NUMBER identifiziert wurde, ist am parser gespeichert und über yytext zugreifbar. dieser wird in eine zahl konvertiert und auf den stack gelegt.
angenommen es wird der String "(PI)" geparst, dann trifft als erstes die regel PI welche e den wert Math.PI zuweist und dann (die regeln können rekursiv sein) die regel '(' e ')'
hier wird e der wert der 2. teilregel zugewiesen. die regel e ist ja definiert als token '(' gefolgt von der teilregel e gefolgt von dem token ')'. (die token '(' und ')' hätte ich LEFTBRACE und RIGHTBRACE genannt, hier heißen sie eben '(' und ')' nicht verwechseln mit strings, das sind tokennamen)
die teilregel e wurde ja im vorhergehenden schritt abgearbeitet und ihr der wert Math.PI zugewiesen. auf dem stack liegt also token '(', MATH.PI und token ')' mit $2 kann auf den wert der teilregel zugegriffen werden und dieser wird wieder e zugewiesen.
der rest sollte jetzt klar sein.

den parser kannst du dier generieren lassen und runterladen. z.b. als test.js. das kannst du einfach über script-tags in dein html einbinden und dann mit var result = parser.parse("text") deinen text parsen lassen.

du brauchst jetzt noch deine felder {00RyWNRMT1}
da würde ich die token '{', '}' und IDENTIFIER definieren, damit PI und E nicht als IDENTIFIER identifiziert werden, muss PI und E vor dem token IDENTIFIER stehen.
dann kannst du eine neue regel fields einfügen
Code:
fields : '{' IDENTIFIER '}'
  {
    $$ = getIdentifierValue($IDENTIFIER); 
  };
du brauchst noch eine funktion getIdentifierValue welche dir den wert sucht. übergeben wird der name(bei "{00RyWNRMT1}" also "00RyWNRMT1").
dafür fügst du diese am anfang der datei zw. %{ und %} als Prologue ein.
Code:
%{
var test = {
  aaa: 7,
  bbb: 3
};
function getIdentifierValue(name)
{
  return test[name];
}
%}
als beispiel lese ich hier das objekt test, wo du die werte herbekommst musst du wissen.
die regel fields musst du jetzt noch als teilregel von e einfügen
Code:
e
    : e '+' e
        {$$ = $1+$3;}
    | e '-' e
        {$$ = $1-$3;}
    | e '*' e
        {$$ = $1*$3;}
    | e '/' e
        {$$ = $1/$3;}
    | e '^' e
        {$$ = Math.pow($1, $3);}
    | '-' e %prec UMINUS
        {$$ = -$2;}
    | '(' e ')'
        {$$ = $2;}
    | NUMBER
        {$$ = Number(yytext);}
    | fields
        {$$ = $1;}
    | E
        {$$ = Math.E;}
    | PI
        {$$ = Math.PI;}
    ;
und schon ist das auch erledigt
das ganze sieht dann vollständig so aus (yytext habe ich mal noch durch die $-entsprechung ersetzt, das ist am anfang vielleicht einfacher wenn man nicht 10 verschiedene schreibweisen benutzt, sondern nur eine)
Code:
%{
var test = {
  aaa: 7,
  bbb: 3
};
function getIdentifierValue(name)
{
  return Number(test[name]);
}
%}

/* description: Parses end executes mathematical expressions. */

/* lexical grammar */
%lex
%%

\s+                       /* skip whitespace */
"PI"                      return 'PI'
"E"                       return 'E'
[0-9]+("."[0-9]+)?\b      return 'NUMBER'
[a-zA-Z_][a-zA-Z0-9_]*    return 'IDENTIFIER'
"*"                       return '*'
"/"                       return '/'
"-"                       return '-'
"+"                       return '+'
"^"                       return '^'
"("                       return '('
")"                       return ')'
"{"                       return '{'
"}"                       return '}'
<<EOF>>                   return 'EOF'
.                         return 'INVALID'

/lex

/* operator associations and precedence */

%left '+' '-'
%left '*' '/'
%left '^'
%left UMINUS

%start expressions

%% /* language grammar */

expressions
    : e EOF
        {return $1;}
    ;

e
    : e '+' e
        {$$ = $1+$3;}
    | e '-' e
        {$$ = $1-$3;}
    | e '*' e
        {$$ = $1*$3;}
    | e '/' e
        {$$ = $1/$3;}
    | e '^' e
        {$$ = Math.pow($1, $3);}
    | '-' e %prec UMINUS
        {$$ = -$2;}
    | '(' e ')'
        {$$ = $2;}
    | NUMBER
        {$$ = Number($1);}
    | fields
        {$$ = $1;}
    | E
        {$$ = Math.E;}
    | PI
        {$$ = Math.PI;}
    ;

fields : '{' IDENTIFIER '}'
  {$$ = getIdentifierValue($2);};
das kannst du hier eingeben, generieren und downloaden.
testen geht dort leider nicht gleich, da der dynamisch generierte parser die eigenen funktionen nicht kennt.
aber mit dem runtergeladenem statischen parser sollte das so problemlos funktionieren:
Code:
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script src="test.js"></script>
    <script src="jquery.js"></script> 
    <script>
      $(document).ready(function () {
        $("button").click(function () {
          try {
            var result = parser.parse($("#source").val())
            $("#out").html(result);
          } catch (e) {
            $("#loading").html(String(e));
          }
        });
      });
    </script>
  </head>
  <body>
    <h2>Test Your Parser</h2>
    <textarea id="source" rows="8" cols="80">(-{aaa}+5)*PI^2</textarea><br/>
    <button>Parse</button>
    <pre id="out"></pre>
    <div id="loading"></div>
  </body>
</html>
 
Zuletzt bearbeitet:
Zurück
Oben