xml einlesen

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.
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Montag 15. August 2005, 14:11

Code: Alles auswählen

for line in open("Datendatei").readlines():
    line = line[SKIPBYTES:]
    feld1 = line[:4]
    feld2 = line[4:]
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Montag 15. August 2005, 20:56

Anonymous hat geschrieben:ich versuche gerade, SAX zu verwenden, leider gibt es ein Problem:
im Buch gibt es ein Beispiel, wie man make_parser zu verwenden kann. Aber für meinen Fall ist parseString etwas sinnvoller (aus Python-dokument). Mit make_parser wird als Default ExpatParser verwendet, der keine parseString vorhanden ist.

Meine Frage, wie kann man ein String (aus XML-tags) parsen?
Bin zwar kein Experte, was SAX angeht, aber parseString findest du folgendermaßen:

Code: Alles auswählen

>>> import xml.sax
>>> xml.sax.parseString
<function parseString at 0x502d99f0>
My god, it's full of CARs! | Leonidasvoice vs Modvoice
TripleH
User
Beiträge: 29
Registriert: Donnerstag 11. Dezember 2003, 12:58

Montag 15. August 2005, 21:24

Hi,


zum Beitrag von Joghurt.
ja soweit war auch meine Idee.
So kann man Zeileninhalt speichern. Den Syntax SKIPBYTES klappt nicht. Brauchman ja auch letztendlich nicht,
Wenn die einzelnen Felder durch Positionsangaben Abgrenzen kann. wie z.B.
feld1 = line[104:108]
feld2 = line[108:112]

So meine Frage ist jetzt ob es nicht einen Syntax gibt der auf dem zuvorigenFeld aufbaut.
Also Praktisch so:

line = line[Skipbytes:104] //erste 100 zeichen werden übersprungen
feld1 = line[lenth:4]
feld2 = line[lenth:4]

Praktisch das anhand der Längenangabe automatisch weitergegangen wird in der Zeile oder zur Not

feld1 = line[104:length=4]
feld2 = line[108:lenth =4]

Diesen Syntax gibs so nicht sonst würd ich nicht so fragen.Aber ich hoff ihr wisst was ich meine.
Ich will den Syntax umgehen das, dass die letzte Postionsabgabe des ersten Feldes die erste
des 2. Feldes ist.

Ich hoffe jemand kann mir helfen.

MFG

Bast
Benutzeravatar
jens
Moderator
Beiträge: 8482
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Dienstag 16. August 2005, 06:47

Ich glaube ein ähnliches Problem hatte ich auch mal, siehe http://www.python-forum.de/viewtopic.php?t=3011

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 16. August 2005, 09:52

Zum ursprünglichen Problem einen Parser zu schreiben, der die XML Datei zu SQL verarbeitet: ich habe nun meinen ersten Parser soweit verbessert, dass er sowohl Reguläre Ausdrücke als auch SAX2 verwenden kann. DOM kommt in späteren Versionen dazu.

Ich bin von der folgenden XML-Datei ausgegangen, die der von ProgChild in großen Teilen ähnlich ist.

Code: Alles auswählen

<?xml version="1.0"?>

<Media>
    <Name>CD Nummer 1</Name>
    <Table>
        <URL>Test.ASC</URL>
        <Name>daten</Name>
        <Description>...</Description>

        <FixedColumn>
            <Name>NUMMER</Name>
            <Numeric/>
    
            <FixedRange>
                <From>97</From>
                <Length>4</Length>
            </FixedRange>
        
        </FixedColumn>
    
        <FixedColumn>
            <Name>Name</Name>
            <AlphaNumeric/>
        
            <FixedRange>
                <From>101</From>
                <Length>12</Length>
            </FixedRange>
        
        </FixedColumn>
    </Table>
</Media>
Das Programm, das diese Datei parsen kann, sieht so aus:

Code: Alles auswählen

#!/usr/bin/env python
# -*- encoding: latin-1 -*-

import re
import xml.sax, xml.sax.handler

def parsedata_re(filename):
    """Parses the XML-file using regular expressions"""
    f = file(filename, 'r')
    
    # regex for matching needed tags: Name and Length
    name_rex = re.compile(r'(?<=\<Name>).*(?=</Name>)')
    len_rex = re.compile(r'(?<=\<Length>).*(?=</Length>)')
    
    currentname = ''
    values = {}
    parse_enabled = False

    for line in f:
        if '<FixedColumn>' in line:
            # only parse inside FixedColumn
            parse_enabled = True
        elif '</FixedColumn>' in line:
            parse_enabled = False
        
        if parse_enabled:
            name = name_rex.findall(line)
            if name != []:
                currentname = name[0]
                values[currentname] = {}
    
            length = len_rex.findall(line)
            if length != []:
                values[currentname]['length'] = length[0]
        
            if '<Numeric/>' in line:
                values[currentname]['type'] = 'INTEGER'
            elif '<AlphaNumeric/>' in line:
                values[currentname]['type'] = 'VARCHAR'
    
    f.close()
    return values

def create_sql(values):
    """Creates a SQL statement"""
    sql = 'CREATE TABLE Daten\n(\n'
    for key, item in values.items():
        sql += '%(name)s %(type)s(%(length)s)\n' % {'name' : key, 'type' : item['type'], 'length' : item['length'] }
    sql += ');'
    return sql

class TablHandler(xml.sax.handler.ContentHandler):
    def __init__(self):
        self.in_name = False
        self.in_fc = False
        self.in_len = False
        
        self.values = {}
        self.currentname = ''
        
    def startElement(self, name, attrs):
        
        # check what tag was opened
        if name == 'FixedColumn':
            # we're in FixedColumn, so we are permitted to parse Name-tags
            self.in_fc = True
        elif name == 'Name' and self.in_fc:
            # only permit parsing if we are in FixedColumn
            self.in_name = True
        elif name == 'Length':
            # we're in Length tag
            self.in_len = True
        elif name == 'Numeric':
            # the empty tag Numeric occured
            self.values[self.currentname]['type'] = 'INTEGER'
        elif name == 'AlphaNumeric':
            self.values[self.currentname]['type'] = 'VARCHAR'
    
    def characters(self, data):
        if self.in_name:
            self.currentname = data
            self.values[self.currentname] = {}
            
        elif self.in_len:
            self.values[self.currentname]['length'] = data
    
    def endElement(self, name):
        if name == 'FixedColumn':
            self.in_fc = False
        elif name == 'Name':
            self.in_name = False
        elif name == 'Length':
            self.in_len = False
        

def parsedata_sax(filename):
    parser = xml.sax.make_parser()
    handler = TablHandler()
    parser.setContentHandler(handler)
    parser.parse(filename)
    return handler.values

def main():
    name = 'tabl.xml'
    #values = parsedata_re(name)
    values = parsedata_sax(name)

    print create_sql(values)

if __name__ == '__main__':
    main()
Ich mag SAX irgendwie trotzdem nicht, gar nicht auszudenken, wie so ein Programm in Java aussehen würde.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
TripleH
User
Beiträge: 29
Registriert: Donnerstag 11. Dezember 2003, 12:58

Dienstag 16. August 2005, 11:02

Hi,

tolle Lösung. Viele Wege führen nach Rom:) Ich werd mal versuchen hinter zu steigen. Kann jemand noch zu meinem zuvorigen Posting schauen? Ich versuche immer noch die erstellte Tabelle zu füllen und häng an der Kleinigkeit. Siehe 2 Postings drüber.

MFG

Basti
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 16. August 2005, 11:45

TripleH hat geschrieben:Ich werd mal versuchen hinter zu steigen. Kann jemand noch zu meinem zuvorigen Posting schauen?
Könntest du uns vielleicht einfache Daten posten und was dann rauskommen soll? So kann man das etwas einfacher nachvollziehen, denke ich.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
TripleH
User
Beiträge: 29
Registriert: Donnerstag 11. Dezember 2003, 12:58

Dienstag 16. August 2005, 17:50

Hi,

Daten selbst habe ich auch nicht. Aber es Geht ja immer noch um die XML. und wenn man jetzt z.B. mit deinem Skript die Struktur der Tabelle in der Datenbank hat wäre es ja wichtig sie auch zu füllen.

hier ist das Posting welches ich zuvor geschrieben...

Dank dir. Progchild. Denk habs verstanden. Ich hab mal nen bicßehn rumgespielt und die mit der Create Anweisung die Tabelle in der Datenbank erstellt.

Jetzt hab ich nur mal ne Frage. Die xml Struktur in eine Datenbank zu schreiben ist nicht so schwer. Nur ich hab ma oben bei Lara gelesen. Ziel einer jeden Tabelle in einer Datenbank ist es ja sie zu füllen. Ich hab jetzt mal zu der Test.xml mal eine sequentielle Datei erstellt.

ganz einfach. sind ja nur 2 Felder
Nummer und Name.

das würde dann wohl so aussehen.

ersten 97 zeichen überlesen 1234siegfreidfre
ersten 97 zeichen überlesen 3456herrmanfre

so könnte ja die test.txt aussehen die durch die test.xml beschrieben wurde. Nur mir ist eine schon theoretisch bei der Umsetztung etwas nicht klar. Ich hoffe jemande kann mir helfen:
Und zwar muss ich mir den ersten Feldblock in der xml suchen und dort finde ich ja die Position wo er in der test.txt anfängt und wie lang er ist.
Nun muss ich mit dieser info in der txt an die Stelle springen und den Feldinhalt wieder speichern für das insert query.
Irgendie hab ich ne Blockade im Kopf wegen den 2 Dateien die jetzt gleichzeitig zu handeln sind.

Kann mir jemand nen Denkanstoss geben?






Daraufhin hat mir Joghurt geantwortet und danach kam mein Posting auf welches ich veriwesen habe..


Ich hoff du blickst durch.

MFG

Basti
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 16. August 2005, 18:22

Es ist schwer zu sagen, wo die Daten in der test.txt sind, es steht nur ein From-Wert drin, dieser bezeichnet was? Die Zeilennummer? Die Stelle an der die Daten ausgelesen werden? Ich könnte mir folgenden Aufbau dieser text.dat (nicht plain-text) vorstellen:

Code: Alles auswählen

datei = 97 * 'a' + 4 * 'b' + 12 * 'c'
Der Aufbau der XML Datei definiert folgendes (ich habe meine RE und SAX-Parser erweitert, so dass sie auch den From-Wert lesen können):

Code: Alles auswählen

{'NUMMER': {'from': '97', 'length': '4', 'type': 'INTEGER'}, 'Name': {'from': '101', 'length': '12', 'type': 'VARCHAR'}}
Dann geht ein Script dieses Dictionary durch, geht erstmal an Position 97, liest 4 Zeichen (Das wären dann im meinem Beispiel die 'b's), dann geht das Script weiter, stellt sich an Position 101, und liest von dort 12 Zeichen (die 'c's in meinem Beispiel). Ist es das was du meinst? Das scheint mir ziemlich einfach zu sein, da ja immer angegeben wird, von welcher Stelle wieviel gelesen werden soll.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Gast

Dienstag 16. August 2005, 19:59

Hi,

also hab dieses Verfahren schon öfters gesehen. Und zwar ist das schon richtig. Man muss mit einer for Schleife Zeile für Zeile in der TXT durchgehen. Die Angaben der xml beziehen sich aber nur auf eine komplette Zeile. Somit wäre der From 99 die Postion in der Zeile wo das Feld beginnt.
Wie gesagt so würde das z.B. aussehen:

ersten 97 zeichen überlesen 1234siegfreidfre
ersten 97 zeichen überlesen 3456herrmanfre

die ersten 97 Zeichen werdn überlesen und 1234 ist dann due Nummer und danach kommt der Name dann.

Deswegen will ich ja vermeiden bei Nummer = line[97:101]
Name = line [101:109]

das irgendwie die Endposition die Anfangspositon des 2. Fedes ist. sondern irgendwie noch die length Angabe aus der xml verwenden.
Oh mensch viel geschrieben. Ich hoff es ist dir jetzt klar wie ichs meine.
Schau ruhig nochmal auf das andere Posting vielleicht drücke ich mich auch falsch aus:)

MFG

Basti
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 16. August 2005, 21:21

Okay, dann versuch ich mal so ein Programm zu schreiben (ohne etwas aus der XML-Datei einzulesen):

tebl.txt (bitte nicht über den Inhalt der Datei meckern, mir ist im Moment nichts sinnvolleres eingefallen)

Code: Alles auswählen

ersten 97 zeichen ueberlesen 1234 blablub blubbla tralala naan zeta erle tuut flufluf tataa arghDATAende
149 2001 hal 9000 ugs
Nun das Programm:

Code: Alles auswählen

#!/usr/bin/env python
# -*- encoding: latin-1 -*-

spec = [['NUMMER', {'from': 97, 'length': 4, 'type': 'INTEGER'}], 
    ['Name', {'from': 10, 'length': 8, 'type': 'VARCHAR'}]]

print 'Opening file'
f = file('tabl.txt', 'r')

print 'Iterating through lines'
for line, specline in zip(f, spec):
    line = line.splitlines()[0]
    print 'Line: %s' % line
    print 'Spec:',
    print specline
    
    from_point = specline[1]['from'] - 1
    to_point = from_point + specline[1]['length']
    
    data = line[from_point:to_point]
    print 'Data: %s' % data

print 'Closing file. EOP'
f.close()
Spec ist die Spezifikation, welche Daten aus der Datei gelesen werden.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
TripleH
User
Beiträge: 29
Registriert: Donnerstag 11. Dezember 2003, 12:58

Dienstag 23. August 2005, 17:39

Hallo,

sorry das ich mich nicht solnage gemeldet habe. Gut ich versuch ja immer noch die Daten
aus der Textdatei in die von der xml Struktur gegebene Tabelle zu importieren.
Gut also ich mit diesem Skript wird jetzt eine Liste erstellt welche ein dictionary beinhaltet
welches dann mit der for Schleife dann durchlaufen wird.

Code: Alles auswählen

import xml.parsers.expat 
import MySQLdb 

class Reader: 
    def read( self, filename ): 
        self.query = '[' 
        self.path = [] 

        p = xml.parsers.expat.ParserCreate() 

        p.StartElementHandler = self.start_element 
        p.EndElementHandler = self.end_element 
        p.CharacterDataHandler = self.char_data 

        f = open("C:\\test.xml", 'r' ) 
        p.ParseFile( f ) 
        f.close() 

    def add_seperator( self ): 
        if not self.first_col: 
            self.query += ", " 
        else: 
            self.first_col = False 

    def start_element( self, name, attrs): 
        path = "/" . join( self.path ) 

        if path == 'Media': 
            if name == 'Table': 
                self.first_col = True 
        
       
        self.path.append( name ) 
        self.cur_data = '' 
    
    def end_element( self, name): 
        path = "/" . join( self.path ) 
        
        
        if path == 'Media/Table/FixedLength/FixedColumn/Name': 
            self.add_seperator() 
            self.query += "['" + self.cur_data + "'," 
                    
        if path == 'Media/Table/FixedLength/FixedColumn/FixedRange/Length':
            self.query += "'length': " + self.cur_data + "}]"

        if path == 'Media/Table/FixedLength/FixedColumn/FixedRange/From':
            self.query += " {'from': " + self.cur_data + ","

        if path == 'Media/Table': 
            self.query += "]" 

        del self.path[-1] 
    
    def char_data( self, data): 
        self.cur_data += data 

    def get_query( self ): 
        return self.query 


r = Reader() 
r.read('test.xml' ) 
spec = r.get_query().encode('latin-1') 
print spec
f = file("C:\\test.txt", 'r')  
for line in f: 
    line = line.splitlines()[0]
    print 'Line: %s' % line 
    for specline in spec:
        
  
        from_point = specline[1]['from'] 
        to_point = from_point + specline[1]['length'] 
    
        data = line[from_point:to_point]
        name = specline[0]
        print 'name: %s' % name 
        print 'Data: %s' % data 

print 'Closing file. EOP' 
f.close()


So sieht die Ausgabe aus:

[['NUMMER', {'from': 99,'length': 5}], ['UNIUE', {'from': 104,'length': 8}], ['SKZSICH','length': 3}], ['DATABR', {'from': 115,'length': 8}]]
Iterating through lines
Line: 12345darfichsbkb20001224

Traceback (most recent call last):
File "C:\Programme\Python2.4\gdpdu1.py", line 74, in -toplevel-
from_point = specline[1]['from']
IndexError: string index out of range


Allerdings kommt er irgendwie nicht hiermit zurecht:spec = r.get_query().encode('latin-1').
Zumindest scheint er das wohl als gesamten string zu sehen oder?
Die For Schleife kann nicht durchlaufen werden. Hat jemand ne Idee woran es liegt?
Das 2. Prob ist wenn die Forschleife durchlaufen wird ist es ja so das dann die Daten
der Felder gespiechert werden müssen. zur Zeit werden ja nur auf der data variablen
gespeichert aber sie müssen ja in in diese Anweisung mit eingebaut werden:

INSERT INTO Stat VALUES('Feldinhalt',Feldinhalt',...)

Hat da jemand ne Idee? Ich Danke für eure Mühe:)

MFG

Basti
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Mittwoch 24. August 2005, 14:55

TripleH hat geschrieben:

Code: Alles auswählen

 [...]
r = Reader() 
r.read('test.xml' ) 
spec = r.get_query().encode('latin-1') 

line = line.splitlines()[0]
print 'Line: %s' % line 
for specline in spec:
        from_point = specline[1]['from'] 
Dein Problem ist, dass specline ein String ist. Du kannst den nicht wie ein Dict behandeln. Ich würde dir vorschlagen, dass du die Informationen restmal als Dict abspeicherst... :roll:
TripleH
User
Beiträge: 29
Registriert: Donnerstag 11. Dezember 2003, 12:58

Donnerstag 25. August 2005, 12:12

hallo,

ja das hab ich mir gedacht das es nen String ist. Ist es denn möglich den String in ein Dictionary umzuwandeln? Weil den Ansatz wie ich das angefangen habe würd ich gern weiterverfolgen, weil die Zeilen die ausgegeben werden sind ja richtig nur das es nen String ist.

mfg

sarah
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Donnerstag 25. August 2005, 13:48

Überall wo

Code: Alles auswählen

self.query += ... 
steht, musst du die Daten halt in ein Dict packen statt sie dem String query hinzuzufügen.
Antworten