xml und asc Files miteinander abgleichen

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.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Hallo zusammen!

Wie im Betreff schon geschrieben steht möchte ich eine xml File einlesen und die Daten welche benötigt werden, werden herausgefiltert.
Anschließend sollen die Daten mit der asc File abgeglichen werden und nur die Zeilen in welcher die Daten vorhanden sind sollen ausgewertet werden.

Um ein xml File auszulesen nutze ich:

Code: Alles auswählen

import xml.sax
Diese Anleitung hat mir sehr geholfen:
https://www.tutorialspoint.com/python3/ ... processing

Und so wie in der Anleitung der Output ist, möchte ich z.B. auf das Element "Type: War, Thriller" zugreifen.
*****Movie*****
Title: Enemy Behind
Type: War, Thriller
Format: DVD
Year: 2003
Rating: PG
Stars: 10
Description: Talk about a US-Japan war
Leider finde ich im Internet nichts :(
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Nichts von dem, was auf der verlinkten Seite steht, macht man so. Schau Dir mal ElementTree an.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Sirius3 hat geschrieben: Montag 29. Juli 2019, 13:20 Nichts von dem, was auf der verlinkten Seite steht, macht man so. Schau Dir mal ElementTree an.
Ich bin so ein noob :roll:

In dieser Anleitung ist das ElementTree beschrieben:
https://www.datacamp.com/community/tuto ... lementtree
Nur kommt bei mir nach dieser Anleitung als Ausgabe nur "rows{}" heraus. Bei dieser xml File sieht das so aus:
<EngSigConfigModel version="1">
<rows>
<EnvSigConfigRow version="1">
Ich möchte aber in die EnvSigConfigRow hinein
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Warum zweitklassige Tutorials googeln, wo es doch eine erstklassige Dokumentation gibt:
https://docs.python.org/3/library/xml.e ... ttree.html
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

kbr hat geschrieben: Montag 29. Juli 2019, 14:54 Warum zweitklassige Tutorials googeln, wo es doch eine erstklassige Dokumentation gibt:
https://docs.python.org/3/library/xml.e ... ttree.html
Ist auch eher zweitklassig, kann auch dadurch mein Problem nicht lösen :?:

So sieht die xml Datei aus:

Code: Alles auswählen

<data>
	<rows>
		<country name="Liechtenstein">
			<rank updated="yes">2</rank>
			<year>2008</year>
			<gdppc>141100</gdppc>
			<neighbor name="Austria" direction="E"/>
			<neighbor name="Switzerland" direction="W"/>
		</country>
		<country name="Singapore">
			<rank updated="yes">5</rank>
			<year>2011</year>
			<gdppc>59900</gdppc>
			<neighbor name="Malaysia" direction="N"/>
		</country>
		<country name="Panama">
			<rank updated="yes">69</rank>
			<year>2011</year>
			<gdppc>13600</gdppc>
			<neighbor name="Costa Rica" direction="W"/>
			<neighbor name="Colombia" direction="E"/>
		</country>
	</rows>
</data>
Mein Code:

Code: Alles auswählen

tree = ET.parse(self.file_xml)
root = tree.getroot()
print(root.tag, root.attrib)
for child in root:
     print(child.tag, child.attrib)
Ausgabe:

Code: Alles auswählen

(base) C:\Desktop\asc_Oliver>python asc_2.py
EngSigConfigModel {'version': '1'}
rows {}
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@JohannX: noob-Sein ist nicht schlimm. Dafür gibt es ja Foren, die schlechte Webseiten von guten Webseiten trennen.

Ja es gibt ein Kindelement, das heißt `rows`. Was ist dir daran unklar?

Was ist denn Dein eigentliches Problem? Was möchtest Du?
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@JohannX: also, bei mir kommt das raus:

Code: Alles auswählen

data {}
rows {}
d.h. das root-element heißt 'data' und verfügt über keine Attribute und mit dem child-element 'rows' verhält es sich genauso. Letzteres verfügt aber auch wieder über child-elemente. Auch über diese kannst Du iterieren. Mittels ElementTree musst Du Dich eben durch die Hierarchie hangeln.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

@Sirius3 und @kbr
Danke euch beiden!
@JohannX: noob-Sein ist nicht schlimm.
Na da bin ich aber froh :D

konnte das Problem soweit schon mal lösen in dem ich 2 for-schleifen gemacht habe:

Code: Alles auswählen

tree = ET.parse(self.file_xml)
root = tree.getroot()
for child in root:
         for year in child.iter('year'):
            print(year.tag, year.attrib)
Doch dann kommt nur:

Code: Alles auswählen

year {}
heraus, der Inhalt wird somit nicht ersichtlich. :?
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Warum glaubst Du, es gäbe nur die Attribute 'tag' und 'attrib'? In der Python-Dokumentation sind alle Attribute sauber aufgelistet und in wenigen Sekunden zu finden. Als Noob dauert es vielleicht etwas länger (ging uns allen so), aber sie sind zu finden. Versuche es einmal mit

Code: Alles auswählen

print(year.tag, year.attrib, year.text)
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Und die doppelte for-Schleife ist in diesem Fall auch unnötig.

Code: Alles auswählen

for year in root.iter('year'):
    print(year.text)
Übrigens, eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 9 oder 3.

Aber wahrscheinlich möchtest Du sowieso soetwas:

Code: Alles auswählen

for country in root.iter("country"):
    print(country.attrib['name'], country.findtext('year'), country.findtext('rank'))
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Vielen Dank, jetzt komm ich der Sache schon näher! 8)

Code: Alles auswählen

print(year.tag, year.text)
Das habe ich gerade gefunden, funktioniert, juhu :)
Und die doppelte for-Schleife ist in diesem Fall auch unnötig.
Stimmt, da gebe ich dir Recht! Hab auch dies nun bereinigt.

Nun möchte ich 3 Listen erstellen und in jeweils der Liste das Richtige Element speichern. Mittels einem Dropdownmenü (comboBox) kann ich aus der Liste (self.liste_name) dann auswählen einen Namen auswählen. Neben dieser comboBox sind 2 weitere Felder (LineEdit), eine für Base ID (self.liste_canID) und Formula (self.liste_formula).

Wenn ich nun bei der comboBox das erste Element von der liste_name nehme, soll automatisch in den beiden LineEdit Feldern das dazugehöre Element ausgefüllt werden.
Suche vergebens im Internet danach... :(

Code: Alles auswählen

      self.liste_name = []
      self.liste_formula = []
      self.liste_canID = []
      tree = ET.parse(self.file_xml)
      root = tree.getroot()
      for name in root.iter('name'):
         print(name.tag, name.text)
         self.liste_name.append(name.text)

      for formula in root.iter('formula'):
         print(formula.tag, formula.text)
         self.liste_formula.append(formula.text)

      for canID in root.iter('canId'):
         print(canID.tag, canID.text)
         self.liste_canID.append(canID.text)

      self.ui.comboBox_1.addItems(self.liste_name)
      self.ui.comboBox_2.addItems(self.liste_name)

      self.ui.line_BaseID_1.setText(Element von self.liste_canID[0])
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich versuche ja immer auch die Struktur des XML im Code zu verwenden. Also nicht einfach alles rekursiv nach <country>-Tags abzusuchen, sondern gezielt den Pfad dazu anzugeben. Und die Dokumentation rät von `attrib` ab wenn man nicht tatsächlich das komplette Wörterbuch benötigt, weil es Implementierungen der ElementTree-API gibt, die beim zugriff jedes mal ein komplettes Wörterbuch mit allen Attributen erstellen.

Code: Alles auswählen

for country in root.iterfind('rows/country'):
    print(country.get('name'), country.findtext('year'), country.findtext('rank'))
Mit `lxml.objectify` kann man das übrigens auch so schreiben:

Code: Alles auswählen

for country in root.rows.country:
    print(country.get('name'), country.year, country.rank)
@JohannX: Grunddatentypen haben in Namen nichts verloren. Und wenn die Daten in den drei Listen elementweise zusammengehören, dann sollten die nicht in drei verschiedenen Listen stecken, sondern in *einer* Liste. Als `namedtuple` oder eventuell auch als Exemplare von einer selbstgeschreibenen Klasse. Auch das völlig voneinander unabhängig zusammensuchen der Werte aus dem XML-Dokument sieht falsch aus. Das wird ja irgendeine Struktur haben womit die drei Elemente zusammengefasst werden – nutze die. Das Laden von Werten aus einer XML-Datei würde ich auch in eine einzeln testbare Funktion schreiben und nicht mitten in den GUI-Code.

Automatisch passiert da nichts. Du musst das entsprechende Ereignis wenn der Benutzer etwas aus der Combobox auswählt mit Code verbinden der die anderen GUI-Elemente entsprechend verändert. Da sucht man auch nicht im Internet nach, sondern schaut in der Qt-Dokumentation was für Signale so eine Combobox bietet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Auch das völlig voneinander unabhängig zusammensuchen der Werte aus dem XML-Dokument sieht falsch aus. Das wird ja irgendeine Struktur haben womit die drei Elemente zusammengefasst werden – nutze die.
Die Frage ist wie?

Code: Alles auswählen

      for name in root.iter('name'):
         print(name.tag, name.text)

      for formula in root.iter('formula'):
         print(formula.tag, formula.text)

      for canID in root.iter('canId'):
         print(canID.tag, canID.text)
So funktioniert das, aber es werden alle 3 Elemente extra gefiltert

Was ich schon gefunden habe https://www.youtube.com/watch?v=SLs2VCzmA9c sieht vielversprechend aus.
Aber zuerst muss ich das Problem lösen um alle 3 Elemente in einem Durchgang zu filtern.

Code: Alles auswählen

for name, formula, canID in root.iter('name', 'formula', 'canId'):
      print(name.tag, name.text)
      print(formula.tag, formula.text)
......
Dieser Code brachte leider keinen Erfolg, da man nur ein Argument nehmen kann
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@JohannX: so wie es __blackjack__ gezeigt hat. Man sucht sich die übergeordnete Struktur, die man durchlaufen kann und fragt darauf die Einträge ab, die einen interessieren. Jetzt hast Du ja plötzlich eine ganz andere XML-Struktur. Wenn Du dazu fragen hast, solltest Du die auch zeigen. Wenn Du `iter` verwendest, dann kann es passieren, dass man Einträge von unterschiedlichen Einträgen vermischt, daher `iter´ nicht verwenden. `iterfind`, `find`, `findtext` sind die Methoden, die stabil eine Struktur abfragen.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Sirius3 hat geschrieben: Dienstag 30. Juli 2019, 13:01 @JohannX: so wie es __blackjack__ gezeigt hat. Man sucht sich die übergeordnete Struktur, die man durchlaufen kann und fragt darauf die Einträge ab, die einen interessieren. Jetzt hast Du ja plötzlich eine ganz andere XML-Struktur. Wenn Du dazu fragen hast, solltest Du die auch zeigen. Wenn Du `iter` verwendest, dann kann es passieren, dass man Einträge von unterschiedlichen Einträgen vermischt, daher `iter´ nicht verwenden. `iterfind`, `find`, `findtext` sind die Methoden, die stabil eine Struktur abfragen.
Das hab ich irgendwie übersehen?

Code: Alles auswählen

tree = ET.parse(self.file_xml)
      root = tree.getroot()
      for EnvSigConfigRow in root.iterfind("rows/EnvSigConfigRow"):
         print(EnvSigConfigRow.findtext('name'), EnvSigConfigRow.findtext('formula'), EnvSigConfigRow.findtext('canId'))
Damit funktioniert es :)

Und mit dem namedtuple sollte ich dann die 3 Werte zugehörig speichern können?
Sprich mein Code sieht dann so aus:

Code: Alles auswählen

self.liste = collections.namedtuple("liste", ['name', 'formula', 'canId'])
tree = ET.parse(self.file_xml)
      root = tree.getroot()
      for EnvSigConfigRow in root.iterfind("rows/EnvSigConfigRow"):
         print(EnvSigConfigRow.findtext('name'), EnvSigConfigRow.findtext('formula'), EnvSigConfigRow.findtext('canId'))
         S = self.liste(EnvSigConfigRow.findtext('name'), EnvSigConfigRow.findtext('formula'), EnvSigConfigRow.findtext('canId'))
Nur wenn ich dann es ausgeben möchte

Code: Alles auswählen

print(self.liste)
Erscheint diese Meldung

Code: Alles auswählen

<class '__main__.liste'>
:shock:
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Was hast du denn gedacht, was dort ausgegeben wird?
Du bindest in Zeile 1 ein namedtuple an den Namen "self.liste". Also eine vereinfachte Klasse zum Halten von Daten.
Ich denke, das was du suchst, bindest du an einen anderen Namen.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

sparrow hat geschrieben: Dienstag 30. Juli 2019, 13:58 Was hast du denn gedacht, was dort ausgegeben wird?
Du bindest in Zeile 1 ein namedtuple an den Namen "self.liste". Also eine vereinfachte Klasse zum Halten von Daten.
Ich denke, das was du suchst, bindest du an einen anderen Namen.
Ich dachte, dass mit

Code: Alles auswählen

self.liste = collections.namedtuple("liste", ['name', 'formula', 'canId'])
ein leeres nametuple erstellt wird und mit

Code: Alles auswählen

print(self.liste)
die gesamte namedtuple ausgegeben wird. Schade, war wohl nichts

Mit

Code: Alles auswählen

print(self.S)
klappt das dann soweit. Das Problem welches nun vorliegt ist, dass anscheinend bei jeder for Schleife die Werte überschrieben werden... Was ja nicht wirklich Sinn macht weil es ja mehr als nur ein "Block" ist :?
Somit bleibt das letzte immer drinnen und die anderes werden überschrieben :?:

Code: Alles auswählen

<data>
	<rows>
		<country name="Liechtenstein">
			<rank updated="yes">2</rank>
			<year>2008</year>
			<gdppc>141100</gdppc>
			<neighbor name="Austria" direction="E"/>
			<neighbor name="Switzerland" direction="W"/>
		</country>
		<country name="Singapore">
			<rank updated="yes">5</rank>
			<year>2011</year>
			<gdppc>59900</gdppc>
			<neighbor name="Malaysia" direction="N"/>
		</country>
		<country name="Panama">
			<rank updated="yes">69</rank>
			<year>2011</year>
			<gdppc>13600</gdppc>
			<neighbor name="Costa Rica" direction="W"/>
			<neighbor name="Colombia" direction="E"/>
		</country>
	</rows>
</data>
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@JohannX: Deine self.liste ist ein namedtuple (das ist ein Datentyp, aber keine Liste), Dein self.S ist eine Instanz davon, und diese legst Du immer wieder neu an und bindest diese an den gleichen Bezeichner. Wie Du korrekt erkennst, werden die Inhalte dadurch überschrieben. Als Lösung könntest Du eine Liste von namedtuples verwenden.

So unschön das auch klingen mag, aber mit sequentiellem googeln und adaptieren von mehr oder weniger gelungenen YouTube-Videos lassen sich keine Programme zusammenstückeln.
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

An der Stelle auch noch mal der Hinweis auf Namen: `liste` und `S` sind keine guten Namen. Und gute Namen sind auch keine ”Kosmetik” sondern *wichtig*, denn daran sieht man auch ob man verstanden hat was man da für Werte hat.

Und es gibt Namenskonventionen bei Python (wie bei den meisten anderen Programmiersprachen), und dem entsprechen hier weder `liste`, noch `S`, noch `canID`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

kbr hat geschrieben: Dienstag 30. Juli 2019, 15:33 @JohannX: Deine self.liste ist ein namedtuple (das ist ein Datentyp, aber keine Liste), Dein self.S ist eine Instanz davon, und diese legst Du immer wieder neu an und bindest diese an den gleichen Bezeichner. Wie Du korrekt erkennst, werden die Inhalte dadurch überschrieben. Als Lösung könntest Du eine Liste von namedtuples verwenden.

So unschön das auch klingen mag, aber mit sequentiellem googeln und adaptieren von mehr oder weniger gelungenen YouTube-Videos lassen sich keine Programme zusammenstückeln.
Das ist leider das Problem, dass es im Internet keine seriösen Dokumentationen über das namedtuples gibt. Es scheint dass es eher so halbherzig gemacht worden ist. :cry:
https://www.geeksforgeeks.org/namedtuple-in-python/ Nichts brauchbares
https://docs.python.org/3.3/library/col ... namedtuple Nichts brauchbares

Und so geht es weiter.....

Ich dachte dass:

Code: Alles auswählen

self.liste = collections.namedtuple("liste", ["name", "formula", "canId"])
dies eine liste von nameduples ist???
Antworten