Rechnungen speichern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Kamik423
User
Beiträge: 93
Registriert: Donnerstag 28. März 2013, 16:53

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
0x4c65742773206d616b652073757265207468617420686973746f7279206e6576657220666f726765747320746865206e616d6520656e746572707269736521
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

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
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@/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
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@mutetella: "eval" kann z.B. so geschrieben werden "getattr(__builtins__,'ev'+'al')".
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.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

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
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

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
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

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.
Antworten