ich will mathematische ausdruecke (zb 1+5*(4-3)) evaluieren
eval() kommt aus sicherheitsgruenden nicht in frage
irgendwelche ideen?
mathematische ausdruecke evaluieren
Einen einfachen kleinen Parser. Ist warscheinlich bei einigen Parsertools schon als Sample zu finden.
Die Suche liefert dazu bestimmt einiges.
Und sonst ist ein
auch ganz einfach.
Die Suche liefert dazu bestimmt einiges.
Und sonst ist ein
Code: Alles auswählen
eval(expr, {}, {})
damit sind allerdings die builtin-funktionen immernoch verfügbar.EyDu hat geschrieben:Und sonst ist ein
auch ganz einfach.Code: Alles auswählen
eval(expr, {}, {})
abhilfe schafft da
Code: Alles auswählen
eval(expr, {"__builtins__": None})
Costi, wenn dir `eval()` unsicher scheint, könntest du den String vorher durch eine Parser schicken und dich so von der Gültigkeit des Ausdrucks überzeugen. Danach könntest du `eval()` immer noch benutzen. Oder du baust den Parser zu einem Interpreter um. Pythons eigener Interpreter (und Bytecode-Compiler) wird aber wohl effizienter sein.
Da ich gerade Spaß an Kombinatorparsern (und kurzen Variablennamen) habe, biete ich folgenden Quelltext (ohne Gewähr als Inspirationsquelle) an:
Damit kann man fast wie eine Grammatik im EBNF-Format einen Parser für einfache mathematische Ausdrücke definieren:
wenn `expression("3+4")[1] == ''` gilt, wird die komplette Eingabe "3+4" vom Parser verstanden und entspricht folglich der Grammatik.
Da meine Parser erlauben, Aktionen für erkannte Fragmente auszuführen, kann man den Wert auch gleich berechnen:
Stefan
Da ich gerade Spaß an Kombinatorparsern (und kurzen Variablennamen) habe, biete ich folgenden Quelltext (ohne Gewähr als Inspirationsquelle) an:
Code: Alles auswählen
class parser:
def action(self, f): self.f = f; return self
def ignore(self): return self.action(lambda v: None)
class word(parser):
def __init__(self, p): self.p, self.f = "\\s*(%s)" % p, lambda r: r
def __call__(self, s):
m = re.match(self.p, s)
if m: return (self.f(m.group(1)), s[m.end(1):])
def token(t):
return word(re.escape(t)).ignore()
class seq(parser):
def __init__(self, *ps): self.ps, self.f = self.t(ps), lambda r: r[0]
def __call__(self, s):
rs = []
for p in self.ps:
r = p(s)
if not r: return None
if r[0] is not None: rs.append(r[0])
s = r[1]
return (self.f(rs), s)
def t(self, ps): return [token(p) if isinstance(p, str) else p for p in ps]
class alt(parser):
def __init__(self, *ps): self.ps, self.f = ps, lambda r: r
def __call__(self, s):
for p in self.ps:
r = p(s)
if r: return r
class rep(parser):
def __init__(self, *ps): self.p, self.f = seq(*ps), lambda r: r
def __call__(self, s):
rs = []
while True:
r = self.p(s)
if not r: return (self.f(rs), s)
rs.append(r[0])
s = r[1]
class forward:
def __lshift__(self, p): self.p = p
def __call__(self, s): return self.p(s)
Code: Alles auswählen
expression = forward()
number = word("\\d+")
primary = alt(number, seq("(", expression, ")"))
factor = alt(seq("-", primary), seq("+", primary), primary)
term = alt(seq(factor, rep("*", factor)), seq(factor, rep("/", factor)), factor)
expression << alt(seq(term, rep("+", term)), seq(term, rep("-", term)), term)
Da meine Parser erlauben, Aktionen für erkannte Fragmente auszuführen, kann man den Wert auch gleich berechnen:
Code: Alles auswählen
def fold(f, e, l):
return fold(f, f(e, l[0]), l[1:]) if l else e
expression = forward()
number = word("\\d+").action(lambda r: int(r))
primary = alt(number, seq("(", expression, ")"))
factor = alt(
seq("-", primary).action(lambda r: -r[0]),
seq("+", primary).action(lambda r: +r[0]),
primary)
term = alt(
seq(factor, rep("*", factor)).action(lambda r: fold(int.__mul__, r[0], r[1])),
seq(factor, rep("/", factor)).action(lambda r: fold(int.__div__, r[0], r[1])),
factor)
expression << alt(
seq(term, rep("+", term)).action(lambda r: fold(int.__add__, r[0], r[1])),
seq(term, rep("-", term)).action(lambda r: fold(int.__sub__, r[0], r[1])),
term)
print expression("1 + 2 * 3")
als minimalist habe ich folgende loesung vorgezogen:
@sma
danke!, wenn ich mal zeit habe schreibe ich alles basierend an deiner loesung um
EDIT:
bug gefixt (siehe nachster post)
Code: Alles auswählen
In [16]: import math
In [17]: namespace = {'__builtins__': None}
In [18]: namespace.update(math.__dict__)
In [19]: eval('8*cos(5)', namespace, {})
Out[19]: 2.26929748370581
danke!, wenn ich mal zeit habe schreibe ich alles basierend an deiner loesung um
EDIT:
bug gefixt (siehe nachster post)
Zuletzt geändert von Costi am Donnerstag 27. März 2008, 00:29, insgesamt 1-mal geändert.
cp != mv
Code: Alles auswählen
__import__('os').system('touch ev1l')
- birkenfeld
- Python-Forum Veteran
- Beiträge: 1603
- Registriert: Montag 20. März 2006, 15:29
- Wohnort: Die aufstrebende Universitätsstadt bei München
Und
funktioniert damit auch immer noch.
(Die "20" ist natürlich Interpreter-spezifisch.)
Code: Alles auswählen
''.__class__.__bases__[0].__bases__[0].__subclasses__()[20]('/etc/passwd', 'r').read()
(Die "20" ist natürlich Interpreter-spezifisch.)
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Und wie wäre es wenn man vorher prüft ob nur Zahlen und "+-*/()" vorkommen?
EDIT: OK, wenn natürlich auch sowas wie "cos" usw. vorkommen können, dann wird es wieder haarig... Naja, man könnte natürlich alle Strings erlauben, die eine math Funktion sind...
EDIT: OK, wenn natürlich auch sowas wie "cos" usw. vorkommen können, dann wird es wieder haarig... Naja, man könnte natürlich alle Strings erlauben, die eine math Funktion sind...