Wort Suchen

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
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

Hayyy,

vorweg, ich bin neu hier und ein blutiger Anfänger in Python!! Ich beschäftige mich seit 1 Monat ca. damit.

Also mein Problem ist wie folgt:

Ich sollte zuerst mit Python ein Programm schreiben, dass automatisch ein Kassenbon(Quittung) in eine Xml-Datei schreibt.
Damit hatte ich kein Problem! Nun kommt folgende "zusatzaufgabe". Ich soll ein neues Programm schreiben, das die Datei "einliest" und überprüft um das Gesamtergebnis stimmt und lediglich die einzige Ausgabe soll sein, ob es stimmt oder nicht.

Folgende Xml-Datei entsteht aus dem ersten Programm:

Code: Alles auswählen

<?xml version='1.0' encoding='UTF-8'?>

<Kassenbon>
	<Position nummer = "1">
		<Artikel>Duschgel</Artikel>
		<Preis>1.99 Euro </Preis>
		<Menge>1</Menge>
		<Summe>1.99 Euro </Summe>
	</Position>
	<Position nummer = "2">
		<Artikel>Zahnpasta</Artikel>
		<Preis>1.49 Euro </Preis>
		<Menge>1</Menge>
		<Summe>1.49 Euro </Summe>
	</Position>
	<Position nummer = "3">
		<Artikel>Milch</Artikel>
		<Preis>0.45 Euro </Preis>
		<Menge>2</Menge>
		<Summe>0.9 Euro </Summe>
	</Position>
	<Position nummer = "4">
		<Artikel>Milka</Artikel>
		<Preis>0.79 Euro </Preis>
		<Menge>1</Menge>
		<Summe>0.79 Euro </Summe>
	</Position>
	<Position nummer = "5">
		<Artikel>Orangensaft</Artikel>
		<Preis>1.19 Euro </Preis>
		<Menge>3</Menge>
		<Summe>3.57 Euro </Summe>
	</Position>
	<Gesamtpreis>8.74 Euro </Gesamtpreis>
</Kassenbon>
(Hier wird die Datei leider nicht "wohlgeformt" angezeigt.)

Ich muss also die Datei einlesen:

Code: Alles auswählen

datei = open("bon.xml", "r")
ich weiß nicht wie ich nun von jeden Artiel die "Summe" addieren kann bzw. ich weiß nicht wie ich ein genau bestimmten Wert(String oder int) aus der Datei lesen kann und den in einer Variabel abespeichern kann.

Für viele bestimmt einfach und schnell gemacht, für mich aber nicht.
Ich weiß nicht welche Methoden ich benutzen soll.
Wenn Ihr eine Methode benutzt, dann macht bitte immer ein Kommentar dazu, was die Methode macht!!

Vielen Dank im Vorraus !!!


Ach dabei soll im neuen Programm ganz am Anfang " Gesamtpreis = 0" gesetzt werden!
Zuletzt geändert von Anonymous am Freitag 2. November 2012, 13:50, insgesamt 1-mal geändert.
Grund: Syntax-Hervorhebung aktiviert
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo Evilsadness,

zum Lesen und Schreiben von xml hat sich unter Python elementtree bewährt.

Code: Alles auswählen

import xml.etree.ElementTree as ET
bon=ET.ElementTree(file='kassenbon.xml')
Die Methoden find und findall helfen dann beim Durchsuchen der xml-Struktur.

Grüße
Sirius
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

So meinte ich das nicht.
Ich soll kein Module implemtieren.
Ich suche irgenwelche Methoden, die z.B. beim Duschgel den Preis rauslesen kann , also irgendwie in der Zeile gehen und dann vllt. mit der splitt Methode die Zeile trennen oder so in der Art. Und dies mit jedem Produkt machen und die Preise Addieren und gucken ob es mit dem Gesamtergenis übereinstimmt.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Verstehe ich das richtig? Du darfst keine Module verwenden? Das klingt aber seltsam.
Nunja, womit hast du denn genau Schwierigkeiten? Wie man Dateien öffnet weißt du, sonst hättest du die Datei nicht speichern können, nehme ich an.

Wenn du da selbst parsen willst, dann helfen die die Methoden für Zeichenketten mit dem Inhalt umzugehen. Damit kann man in Zeichenketten suchen und so auch lesen.
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

ich darf schon mit Modulen arbeiten, jedoch sollten wir es ohne versuchen.

Super seite!!! Nach sowas habe ich gesucht!
Gibt es vllt. Lösungsvorschläge?
Versuche mich morgen mittag nochmal ran, jetzt habe ich leider keine Zeit mehr dafür.
BlackJack

@Evilsadness: XML sollte man ohne eine vernünftige XML-Bibliothek weder schreiben noch lesen. Das ist kein einfaches Textformat, auch wenn es auf den ersten Blick so aussehen mag.

Man kann sehr leicht beim Schreiben Fehler machen, so dass die Datei von keinem anderen Programm, welches XML erwartet, mehr gelesen werden kann.

Andersherum ist es sehr aufwändig selbst Code zu schreiben, der wirklich XML lesen und verarbeiten kann und nicht nur eine Untermenge von wohlgeformten XML.

Es gibt aber einfach zu verwendende Bibliotheken, die wohlgeformtes XML erzeugen und lesen können. Es macht also wenig Sinn darauf zu verzichten und stattdessen das Rad neu zu erfinden, nur dass es zwangsläufig ein kaputtes, eckiges Rad sein wird.

Lesehinweis: Mindestens der Abschnitt „Don’t think of XML as a text format” in HOWTO Avoid Being Called a Bozo When Producing XML.

Wenn Dein Programm XML lesen können soll, musst Du auch immer damit rechnen, dass die Datei durch irgendein XML-Werkzeug bearbeitet wurde und in den Grenzen der XML-Spezifikation anders gespeichert sein kann, ohne dabei vom Inhalt her verändert worden zu sein. Die Zeichenkodierung kann verändert werden, einzelne (Sonder)zeichen können als Character-Entitäten serialisiert werden, Text kann teilweise in CDATA-Blöcke eingwfasst sein, „whitespace” kann verändert worden sein, Kommentare können hinzugefügt oder gelöscht worden sein, und so weiter.

Falls Du beim XML-Beispiel keinen Code-Block im Beitrag verwendet hast, wäre er übrigens weiterhin wohlgeformt dargestellt worden! Solange es kein Schema für den Kassenbon gibt, der „whitespace” in Textknoten als signifikant deklariert wäre es sogar zusätzlich auch valides XML alles in einer Zeile zu schreiben oder innerhalb von „whitespace” Umbrüche zu machen.

Das Kassenbon-Format selber hat das Problem, dass die Geldbeträge nicht nur die Zahl, sondern auch die Währung enthalten. Das ist ungünstig, weil es unnötige Arbeit beim Verarbeiten verursacht. Man hätte auch mehr über Attribute repräsentieren können. Und die Währung pro Position oder gar für den gesamten Kassenbon angeben können. So ist das ziemlich redundant und sieht eher wie ein Dokument zur einfachen Darstellung als eines zum weiterarbeiten mit den Daten aus.
Benutzeravatar
StefanLawl
User
Beiträge: 92
Registriert: Donnerstag 7. Juni 2012, 20:23

Nunja, ich weiß nicht ob du das meinst, aber ich wenn du keine Module importieren darfst (/willst), würde ich die Datei eventuell so auslesen:

Code: Alles auswählen

datei = open("bon.xml", "r")
for line in datei:
     print line
Würdest du jetzt alle Preise ausgeben wollen, ginge das z.B. so:

Code: Alles auswählen

datei = open("bon.xml", "r")
preise=[]
for line in datei:
     if 'Preis' in line:
          preise.append(line[13:17])
print preise
Das Problem dabei ist aber, dass du nicht anständig damit arbeiten kannst, da es speziell nur für diese Datei verwendbar ist ( und es ist einfach nicht "schön" :P) Bin selbst noch neu mit Python, ich hoffe das konnte dir trotzdem auf irgendeine Weise helfen.
Man sagt uns wir sollen der Idee gedenken und nicht des Mannes. Denn ein Mensch kann versagen. Er kann gefangen werden. Er kann getötet und vergessen werden. Aber 400 Jahre später kann eine Idee immer noch die Welt verändern.
-V
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

StefanLawl hat geschrieben:Nunja, ich weiß nicht ob du das meinst, aber ich wenn du keine Module importieren darfst (/willst), würde ich die Datei eventuell so auslesen:

Code: Alles auswählen

datei = open("bon.xml", "r")
for line in datei:
     print line
Und ich poste es zum wiederholten Male ( ;-) )unter Deine ``open``-Konstrukte: Dateien sollte man immer mittels ``with`` öffnen!

Code: Alles auswählen

with open(...) as handler:
    for line in handler:
        print line
Das ist wesentlich sicherer, da die Datei in jedem Falle geschlossen wird - also auch beim Auftreten einer Exception. Zudem spart man sich das explizite ``handler.close()`` und vergisst das Schließen damit nicht; so wie Du gerade eben :P

@Evilsadness: In welchem Kontext müsst ihr das denn lösen? Ich würde mal behaupten, dass man an einer Uni jeden Assistenten / Hiwi dazu bekommt, einem die Übung abzunehmen, wenn man mit stichhaltigen Argumenten kommt! Und das Schreiben eines eigenen XML-Parsers / -Generators ist ja wohl eher nicht Sinn eurer Aufgabe, sondern das Lernen von Grundlagen. Damit reduziert es sich auf das elementare Verständnis von gängigen Datenstrukturen und der Möglichkeiten, Funktionen richtig zu kombinieren. Das Benutzen einer fremden Bibliothek spricht imho eher dafür, dass man dieses Verständnis erworben hat, denn das (schlechte) Implementieren eines eigenen XML-Parsers mit den üblichen, schon zig Mal in Übungen durchgekauten Funktionen und Methoden...

An der Schule sieht es da evtl. ein wenig düsterer aus, da Du selber diese Argumentation gegenüber Deinem Lehrer vortragen müsstest... das kann je nach Alter auch in Sachen Selbstbewusstsein schwierig sein ;-)

Eines kannst Du aber immer machen: Demjenigen dieses Forum nennen, dann kann er sich ja mit uns "anlegen" und über diese wenig sinnvollen Anforderungen diskutieren :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

Parsen und erzeugen von solchen Kassenbon-XML-Dokumenten mit einer ordentlichen XML-Bibliothek, die gültiges XML erzeugt und auch jeden Kassenbon in gültigem XML lesen kann:

Code: Alles auswählen

#!/usr/bin/env python
from decimal import Decimal
try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree


class ReceiptError(Exception):
    pass


class Amount(object):
    def __init__(self, value, currency):
        self.value = value
        self.currency = currency
    
    def __str__(self):
        return '%s %s' % (self.value, self.currency)
    
    def __eq__(self, other):
        return (self.value, self.currency) == (other.value, other.currency)
    
    def __ne__(self, other):
        return not (self == other)
    
    def __add__(self, other):
        if self.currency != other.currency:
            raise ValueError('currency mismatch')
        return Amount(self.value + other.value, self.currency)
    
    def __radd__(self, other):
        return self if other == 0 else NotImplemented
    
    def __mul__(self, other):
        return Amount(self.value * other, self.currency)
    
    @classmethod
    def from_string(cls, string):
        amount, currency = string.split(None, 1)
        return cls(Decimal(amount), currency.strip())


class Position(object):
    TAG = 'Position'
    DESCRIPTION_TAG = 'Artikel'
    PRICE_TAG = 'Preis'
    COUNT_TAG = 'Menge'
    TOTAL_TAG = 'Summe'
    NUMBER_ATTRIBUTE = 'nummer'
    
    def __init__(self, description, price, count=1):
        self.description = description
        self.price = price
        self.count = count
    
    def __str__(self):
        return '%15s %3d x %s = %s' % (
            self.description, self.count, self.price, self.total
        )
    
    @property
    def total(self):
        return self.price * self.count
    
    def as_xml(self, number):
        result = etree.Element(self.TAG, {self.NUMBER_ATTRIBUTE: str(number)})
        for tag, value in [
            (self.DESCRIPTION_TAG, self.description),
            (self.PRICE_TAG, self.price),
            (self.COUNT_TAG, self.count),
            (self.TOTAL_TAG, self.total),
        ]:
            node = etree.SubElement(result, tag)
            node.text = unicode(value)
        
        return result
    
    @classmethod
    def from_xml(cls, node, expected_number=None):
        if expected_number is not None:
            number = int(node.get(cls.NUMBER_ATTRIBUTE))
            if number != expected_number:
                raise ReceiptError(
                    'expected number %d, got %d' % (expected_number, number)
                )
        
        result = Position(
            node.find(cls.DESCRIPTION_TAG).text,
            Amount.from_string(node.find(cls.PRICE_TAG).text),
            int(node.find(cls.COUNT_TAG).text)
        )
        
        if result.total != Amount.from_string(node.find(cls.TOTAL_TAG).text):
            raise ReceiptError(
                'total at position %d is incorrect' % expected_number
            )
        
        return result


class Receipt(object):
    TAG = 'Kassenbon'
    TOTAL_TAG = 'Gesamtpreis'
    
    def __init__(self):
        self.positions = list()
    
    def __str__(self):
        result = ['%d. %s' % (i, p) for i, p in enumerate(self, 1)]
        result.append('Total: %s' % self.total)
        return '\n'.join(result)
    
    def __len__(self):
        return len(self.positions)
    
    def __iter__(self):
        return iter(self.positions)
    
    @property
    def total(self):
        return sum(position.total for position in self)
    
    def add(self, position):
        self.positions.append(position)
    
    def as_xml(self):
        result = etree.Element(self.TAG)
        for number, position in enumerate(self, 1):
            result.append(position.as_xml(number))
        total_node = etree.SubElement(result, self.TOTAL_TAG)
        total_node.text = str(self.total)
        return result
    
    @classmethod
    def from_xml(cls, node):
        result = cls()
        
        for expected_number, position_node in enumerate(
            node.findall(Position.TAG), 1
        ):
            result.add(Position.from_xml(position_node, expected_number))
        
        total = Amount.from_string(node.find(cls.TOTAL_TAG).text)
        if result.total != total:
            raise ReceiptError(
                'expected total %s, got %s' % (result.total, total)
            )
        
        return result


def main():
    receipt_node = etree.parse('test.xml')
    try:
        receipt = Receipt.from_xml(receipt_node)
    except ReceiptError as error:
        print 'Error:', error
    else:
        print receipt
        print
        etree.dump(receipt.as_xml())
        print


if __name__ == '__main__':
    main()
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

@ StefanLawl PERFEKTT!!!! Genau nach sowas habe ich gesucht!!! ich weiß es ist ziemlich "sinnlos" mit sowas zuarbeiten, also ein Programm schreiben, für nur "diese" Datei, aber wir sollen halt mit herum versuchen und probieren uns besser in python reinarbeiten!

Habe es nun Fertig und sieht wie folgt aus:

Code: Alles auswählen

datei = open("bon.xml", "r")
preise=[]
gesamtpreis =""
for line in datei:
    if "Summe" in line:
          preise.append(line[9:13])
    if "Gesamtpreis" in line:
        gesamtpreis = line[14:18]
print ("Einzelne Preise: " + str(preise))
print ("Gesamtpreis in der Datei: " + gesamtpreis + " Euro")


i= len(preise)
Gesamtpreis = 0
for i in range(0,i,1):
    Gesamtpreis = Gesamtpreis + float(preise[i])
print ("Der Gesamtpreis der vom Programm ausgerechnet wurden ist: " + str(Gesamtpreis)+"\n\n")

if str(Gesamtpreis) == gesamtpreis:
    print ("Die Preise Stimmen über ein!!")
else:
    print("Leider stimmen die Preise nicht überein!")
Vieeelen Dank!!!!!!!!!!!
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo Evilsadness,

ein paar Anmerkungen:
datei wird nicht mehr geschlossen. Die einfachste und sicherste Möglichkeit ist
ein with-Block um die ganze Einleseschleife:

Code: Alles auswählen

with open("bon.xml", "r") as datei:
  for ...
Dein line[9:13] funktionier spätestens bei Preisen ab 10€ nicht mehr.
Ein line[line.index('<Summe>')+7:line.index('Euro')] ist wohl das mindeste.

Eine for-Schleife in Python iteriert immer über Listen oder ähnliches.
Der Umweg über range ist unsinnig.

Code: Alles auswählen

for preis in preise: ...
oder kürzer:

Code: Alles auswählen

Gesamtpreis = sum(map(float,preise))
Die Abfrage, ob str(Gesamtpreis)==gesamtpreis ist, wird nicht immer funktionieren.
Irgendwann gibt es Rundungsfehler und aus 4.25 wird 4.2499999999998.

Grüße
Sirius
Benutzeravatar
StefanLawl
User
Beiträge: 92
Registriert: Donnerstag 7. Juni 2012, 20:23

Code: Alles auswählen

i= len(preise)
for i in range(0,i,1):
Wäre besser:

Code: Alles auswählen

for i in xrange(len(preise))
Wobei Sirius' Lösung wohl am besten wäre

Code: Alles auswählen

Gesamtpreis = sum(map(float,preise))
Man sagt uns wir sollen der Idee gedenken und nicht des Mannes. Denn ein Mensch kann versagen. Er kann gefangen werden. Er kann getötet und vergessen werden. Aber 400 Jahre später kann eine Idee immer noch die Welt verändern.
-V
Evilsadness
User
Beiträge: 29
Registriert: Freitag 2. November 2012, 13:11

Abend Serius3,

Verbesserungen nehme ich geeerrnnen auf!!! Vielen dank!!!
aber habe noch paar Fragen:

1:
ich kann doch anstatt

Code: Alles auswählen

with open("bon.xml", "r") as datei:
die datei am ende schließen.. ich hatte es leider bei mir vergessen.
also am ende noch

Code: Alles auswählen

datei.close()
2:

Code: Alles auswählen

 line[line.index('<Summe>')+7:line.index('Euro')]
also nach dem ":" line.index(' Euro') ist es ja verständlich, dass man bis " Euro" Zeile geht,
jedoch wieso am anfang line.index('<Summe>')+7 ?!?! ist doch einfacher wenn man das so schreibt:

Code: Alles auswählen

preise.append(line[9:line.index(" Euro")])
Weil der tag "('<Summe>')" ändert sich eh nicht, also kann ich ja in zeile 9 anfangen.

3:

Code: Alles auswählen

Gesamtpreis = sum(map(float,preise))
sum() = "summiert die Werte in den klammern" --- oder?
map() =???

4:
was ist xrange?
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Evilsadness": Das Äquivalent zu

Code: Alles auswählen

with open("bon.xml", "r") as datei:
    ....
ist nicht

Code: Alles auswählen

datei = open("bon.xml", "r")
...
datei.close()
sondern

Code: Alles auswählen

datei = open("bon.xml", "r")
try:
    ...
finally:
    datei.close()
Gerade um solche Konstrukte unnötig zu machen gibt es ja das with Statement.
Zuletzt geändert von pillmuncher am Sonntag 4. November 2012, 16:55, insgesamt 1-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo,

genau wegen der Aussage: Ändert sich eh nicht, schreibst Du

Code: Alles auswählen

line[line.index('<Summe>')+7:line.index('Euro')]
weil Du Dir immer sicher bist dass vor Summe genau 2 Leerzeichen stehen??
Es ist einfach guter Programmierstil keine festen Zahlen zu verwenden, wenn es
möglich ist, die Zahl durch eine beschreibende Operation zu ersetzen.
Irgendwann später ändert sich eine Voraussetzung und dann fragst Du Dich,
was soll die 9 da? Warum funktioniert die Routine jetzt nicht mehr?


2. map wendet die Funktion float auf jedes Element der Liste preise an und erzeugt
daraus eine neue Liste.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Evilsadness hat geschrieben:Abend Serius3,
...
1:
ich kann doch anstatt

Code: Alles auswählen

with open("bon.xml", "r") as datei:
die datei am ende schließen.. ich hatte es leider bei mir vergessen.
also am ende noch

Code: Alles auswählen

datei.close()
Du solltest Dir abgewöhnen, die Kommentare anderer Benutzer zu ignorieren. Ich hatte Dir dazu schon weiter oben etwas geschrieben, Pillmuncher hat es dann noch einmal ausführlicher erklärt, aber im Grunde stand da schon, *wieso* man ``with`` verwenden sollte.
StefanLawl hat geschrieben: Code:

Code: Alles auswählen

i= len(preise)
for i in range(0,i,1):
Wäre besser:
Code:

Code: Alles auswählen

for i in xrange(len(preise))
Das ist im Grunde genommen nur marginal besser und somit genau so schlecht ;-)

In Python iteriert man über *Objekte* und nicht über Indizes für Objektzugriffe. Man braucht ganz selten *nur* einen Index. Braucht man Index und Objekt, gibt es die ``enumerate``-Funktion, die einem einen Index zu einem Iterable erzeugt.

Noch einmal deutlich:

Code: Alles auswählen

for i in range(len(data)):
    # irgend etwas mit ``data[i]``
Das ist ein absoluter Anti-Pattern in Python!!! Solchen Code sollte man *nie* schreiben.
EvilSadness hat geschrieben: 3:
Code:
Gesamtpreis = sum(map(float,preise))

Code: Alles auswählen

sum() = "summiert die Werte in den klammern" --- oder?
map() =???
``map`` wendet das im ersten Argument angegebene Callable (hier die Funktion ``float``) jeweils auf alle Elemente der Iterables im zweiten Argument an.

Man kann das auch vielleicht zunächst verständlicher so formulieren:

Code: Alles auswählen

sum(float(preis) for preis in preise)
Oder zunächst als reine List-Comprehension:

Code: Alles auswählen

[float(preis) for preis in preise]
Du kannst das aber auch in der Doku nachlesen; ``map`` gehört zu den Built-in Funktionen und sollte daher für Dich schnell zu finden sein ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten