Seite 1 von 1

Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 04:11
von Kamik423
Hallo,

Ich möchte rechnungen wie

Code: Alles auswählen

funktion(5) + 2*x -4

def function()
Int x

Als teil eines txt files speichern, wie kann ich das als gleichung speichern, (x soll änderbsr sein) oder von einem string in eine Gleichung umwandeln

(Wenn ich unverständliches zeug rede, einfach schreiben)

Danke

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 09:27
von mutetella
Wahrscheinlich werde ich dafür gesteinigt...

Code: Alles auswählen

>>> def calc(calculation, x):
...     def funktion(x):
...         return x
...     return eval(calculation)
... 
>>> calc('funktion(5) + 2 * x - 4', 6)
13
>>> calc('funktion(5) + 2 * x - 4', 3)
7
Ansonsten fallen mir noch das functools und das parser Modul ein.

mutetella

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 09:54
von Sirius3
@Kamik423: In Text-Files kannst Du so ziemlich jeden Text schreiben, ob der nun ein "x" enthält oder nicht ist egal. Wenn "x" geändert werden soll, mußt Du allerdings die komplette Datei einlesen, Deine Änderungen durchführen (z.B. content = content.replace('x','y')) und die Datei wieder schreiben.
Im Strings in eine "Gleichung" umzuwandeln, brauchst Du einen Parser. Dazu mußt Du aber erst einmal wissen, was eine Gleichung ist: Baum, Liste in pre-/postfix Notation, oder was ganz anderes.

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 10:34
von /me
mutetella hat geschrieben:Wahrscheinlich werde ich dafür gesteinigt...
Virtuell schon. Und wenn es ginge auch real.

Man sollte niemals eval verwenden ohne darauf hinzuweisen, dass man sich damit ein riesiges Sicherheitsloch in das Programm einbaut.

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 12:46
von mutetella
@/me
Mein erster Impuls war auch, das irgendwie mit `parser` zu lösen, wobei ich damit nicht wirklich weitergekommen bin. Also müsste man den String parsen und dabei Funktionsaufrufe, Operatoren und Konstanten herausziehen. Wobei das wiederum `parser` ja auch macht, mir ist nur nicht klar, ob und wie man an die einzelnen Elemente herankommt.

Wenn ich aber nun bei meiner Lösung mit `eval` bliebe, dann lässt sich das Sicherheitsproblem doch durch

Code: Alles auswählen

nogos = ('import', 'eval', ';')
if len(calculate) < 80:
    for nogo in nogos:
        if nogo in calculate:
            raise AssError
else:
    raise ValueError
um einiges entschärfen, oder nicht?

mutetella

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 12:54
von Sirius3
@mutetella: "eval" kann z.B. so geschrieben werden "getattr(__builtins__,'ev'+'al')".

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 12:59
von BlackJack
@mutetella: Anderseits können die `nogos` ja auch ganz harmlos in literalen Zeichenketten stehen, das heisst Du verbietest damit auch Sachen die der Benutzer ganz legitim eingeben können möchte.

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 13:26
von mutetella
Ok..., echt blöd..., und jetzt? Heißt das, dass `eval` mit strings, die "von außen" kommen generell nicht verwendet wird? Und in diesem Fall wirklich nur ein "händisches" Parsen weiterhilft?

mutetella

Re: Rechnungen speichern

Verfasst: Donnerstag 9. Januar 2014, 13:38
von /me
mutetella hat geschrieben:Ok..., echt blöd..., und jetzt? Heißt das, dass `eval` mit strings, die "von außen" kommen generell nicht verwendet wird?
Wenn du völlig sicher bist, dass das Programm nur von Leuten verwendet wird denen Bösartigkeiten nie in den Sinn kämen, dann kannst du eval verwenden. Das ist typischerweise aber nur der Einsatz beim Entwickler selber.

Re: Rechnungen speichern

Verfasst: Freitag 10. Januar 2014, 08:37
von mutetella
Es lässt mir dann doch einfach keine Ruhe... :wink:

Wie könnte denn ein regulärer Ausdruck in diesem Fall aussehen?

Code: Alles auswählen

expr = (
    r'([a-z]+(?=\(\d+\)))*'             # function name
    r'((?<=[a-z0-9]\()\d+(?=\)))?'      # function argument
    r'([\+\-\/\*])?'                    # operator
    r'((?<=[\-\+\/\*\ ])[a-z0-9]+)?'    # constant
)

Code: Alles auswählen

In [210]: calculation = '(34-3) + funktion(5) + (2*x) -4 / bla(34)'

In [211]: re.findall(expr, calculation)
Out[211]: 
[('', '', '', ''),
 ('', '', '', ''),
 ('', '', '', ''),
 ('', '', '-', '3'),
 ('', '', '', ''),
 ('', '', '', ''),
 ('', '', '+', ''),
 ('', '', '', ''),
 ('funktion', '', '', ''),
 ('', '', '', ''),
 ('', '5', '', ''),
 ('', '', '', ''),
 ('', '', '', ''),
 ('', '', '+', ''),
 ('', '', '', ''),
 ('', '', '', ''),
 ('', '', '', ''),
 ('', '', '*', 'x'),
 ('', '', '', ''),
 ('', '', '', ''),
 ('', '', '-', '4'),
 ('', '', '', ''),
 ('', '', '/', ''),
 ('', '', '', ''),
 ('bla', '', '', ''),
 ('', '', '', ''),
 ('', '34', '', ''),
 ('', '', '', ''),
 ('', '', '', '')]
Dabei werden jetzt allerdings noch keine Klammern, die zusammengehörende Operationen umschließen, berücksichtigt. Zudem werden Funktionsnamen, die z. B. mit einem '+' beginnen als Konstanten erkannt.

Sch... regular expressions.... :( Die werd' ich in meinem Leben nicht mehr in Griff bekommen!

mutetella

Re: Rechnungen speichern

Verfasst: Freitag 10. Januar 2014, 09:17
von Sirius3
Reguläre Ausdrücke, zumindest, wenn man nicht die ganz exotischen nimmt, kennen keine Rekursion, die Du bräuchtest, um zusammengehörige Klammern zu finden. Die Weiterverarbeitung eines solchen Ausdrucks wäre aber dann auch entsprechend komplex.
Deine jetziger regulärer Ausdruck ist ziemlich seltsam: Du sucht einen optionalen Funktionsnamen gefolgt von einem optionalen Argument, gefolgt von einem usw. Zudem ist die Syntax Deines Expression-Parsers seltsam: Funktionsnamen sind nur, wenn danach ein Argument als Zahl folgt? Funktionsargumente sind etwas anders als normale Zahlen? Zahlen sind nur Zahlen wenn sie auf einen Operator folgen?

Grundsätzlich durchmischt Du hier den Lexer mit einem Parser, den Du auch als regulären Ausdruck zu schreiben versuchst. Dabei ignorierst Du alles, was ein Syntaxfehler ist, weil er nicht von Deinem regulären Ausdruck gematcht wird.

Schreib als erstes einen Lexer, der einen Zeichenstrom in Token (Zahl, Name, Operator, Klammer, sonstiges) aufspaltet. Das kannst Du mit einem regulären Ausdruck machen. Die Aufgabe des Parsers erfordert aber mehr Logik, und muß deshalb in Python geschrieben werden.