Auslesen von Werten aus Datei (diesmal kein xml ;-) )

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
mocca
User
Beiträge: 84
Registriert: Mittwoch 4. März 2009, 16:44

Hi,
Ich dachte ich mache mal nen neuen Thread auf da es sich nun doch um komplett eigenständigen code handeln soll.

Also, Ausgangssituation.
Ich habe eine Datei namens lamedb, in der Infos stecken, die ich haben will.

Der Aufbau der Datei ist folgender:

Code: Alles auswählen

transponders
0028ad55:0001:ffff
	s 11605000:2894000:1:3:40:2:0
/
00c00000:03f3:0001
	s 11361750:22000000:0:2:192:2:0:1:2:0:2
/
014ab0ea:0000:0001
	s 12522000:27500000:1:4:330:2:0
/
end
services
2b66:00c00000:03f3:0001:25:0
ZDF HD
p:ZDFvision
2b5c:00c00000:03f3:0001:25:0
Das Erste HD
p:ARD,c:00177a,c:011784,c:02178e,c:03177a,c:050001
end
also, im prinzip eine umrahmung von

Code: Alles auswählen

transponder
end

Code: Alles auswählen

services
end
nehmen wir ein beispiel, was ich nun brauche. nehmen wir "Das Erste HD".
Die Werte für den Sender und für jeden anderen stecken in 3 Zeilen, wobei die erste Zeile immer aus 6 Werten getrennt durch ":" besteht, die zweite auch leer sein kann aber dann eben auch als "nichts" eingetragen werden muss und die 3te zeile kann, muss aber nichts enthalten, muss aber ebenfalls vorhanden sein.
also, immer 3 zeilen pro sender.

Code: Alles auswählen

2b5c:00c00000:03f3:0001:25:0
Das Erste HD
p:ARD,c:00177a,c:011784,c:02178e,c:03177a,c:050001
die infos hierin enthalten nicht alles, was ich brauche. sondern zu dem sender gehört exakt EIN transponder. die transpondernummer ist der dritte wert. hier also dieser wert korrespondiert mit einem transpondereintrag.
transpondereinträge sind IMMEr zwei zeilen, die wiederum vom nächsten transponder durch "/" getrennt sind.
der zugehörige transponder für unser beispiel wäre

Code: Alles auswählen

00c00000:03f3:0001
	s 11361750:22000000:0:2:192:2:0:1:2:0:2
also, der zweite wert beim transponder entspricht dem dritten wert beim service, also sender.

ich müsste also irgendwie zwischen "services" und dem folgenden "end" alle dreizeiler auslesen und am besten alle werte, die durch ":" oder einen zeilenumbruch getrennt sind, in eine neu sortierte zeile speichern.
dazu muss zu dem jeweiligen sender der transponder gesucht werden und dessen werte, die ebenfalls durch ":" und zeilenumbruch getrennt sind, allen sendern mit diesem wert zugewiesen werden.
sodass im prinzip alle transponderinfos und senderinfos am ende nur noch in einer zeile pro sender stehen.

in meinem beispiel haben "das erste hd" und "zdf hd" den gleichen transponder ("03f3") und brauchen somit beide sämtliche werte dieses transponders.

sie würden also am ende so aussehen (das ist eine lange zeile, falls das in eurem browser mit zeilenumbruch steht):

Code: Alles auswählen

namespace#frequency#symbol_rate#......
ich glaub das ist für euch viel schwerer zu verstehen als am ende zu programmieren :D :D


an sich glaube ich sind die meisten grundlagen hier schon drin.
nur wie ich daraus nun eben ne neue datei mit den ganzen angaben erzeuge, die alle in einer zeile liegen, bin ich nicht sicher

hier mal der code:
http://www.python-forum.de/pastebin.php?mode=view&s=15
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Zeichen dafür, etwas verstanden zu haben und programmieren zu können ist häufig, es auch exakt beschreiben zu können. An "schwerer zu verstehen als zu programmieren" glaube ich nicht so recht. Deiner Beschreibung kann ich nicht so ganz folgen, weil ich (außer der Vermutung, es dreht sich irgendwie um Satellitenfernsehen) die Dateiformate nicht kenne. Wenn es aber einfach nur darum geht, das Zeug zwischen den transponder/end und services/end einzulesen, biete ich folgenden Hack. Hack deswegen, weil doch viele "magische" Konstanten enthalten sind und besser Exemplare von zwei Klassen Service und Transponder erzeugt werden sollten, an deren Attributen man dann ablesen kann, was die Zahlen bedeuten.

Code: Alles auswählen

transponders = {}
services = {}

def parse_lamedb(itr):
    for line in itr:
        if line == "transponders":
            break
    for line in itr:
        if line == "end":
            break
        s = next(itr)
        assert next(itr) == "/"
        transponders[line.split(":")[1]] = (line, s)
    for line in itr:
        if line == "services":
            break
    for line in itr:
        if line == "end":
            break
        line2 = next(itr)
        line3 = next(itr)
        services[line2] = (line, line3)

def transponder(service_name):
    return transponders[services[service_name][0].split(":")[2]][1][5:].split(":")

parse_lamedb(....)
print("#".join(transponder("ZDF HD")))
Zu deinem Programm noch ein paar stilistische Anmerkungen: In Python bevorzugt man "snake_case" statt "CamelCase" für Namen. Warum spaltest du für das Einlesen der DB einen eigenen Thread ab?! Den Dateinamen hart zu verdrahten ist keine gute Idee. In deiner Beschreibung hast du eine Versionsnummer unterschlagen. Nutze "!=" statt "<>", welches deprecated ist. Ein Leerschritt hinter einem Komma würde die Lesbarkeit erhöhen. Statt alles erst in einen puffer zu schreiben, kannst du doch gleich in die Datei schreiben. Und benutze dort ein "with:" oder bei antiken Python-Versionen wenigstens try/finally, um die Datei auch im Fehlerfall zu schließen. Gilt übrigens auch für's Einlesen. Ab writeLamedb sind deine Methoden viel zu lang. Versuche mal, nie mehr als 10 bis maximal 20 Zeilen zu benutzen. Insbesondere translateTransponders ist so lang, dass ich keine Lust habe, sie mir anzusehen.

Was ich an deinem Posting nicht verstanden habe, wolltest du nur funktionierenden Code zeigen oder hattest du auch noch eine Frage? Falls nicht, eine Antwort hast du jetzt trotzdem bekommen ;)

Stefan
mocca
User
Beiträge: 84
Registriert: Mittwoch 4. März 2009, 16:44

ich dacht es ist vielleicht übersichtlicher, wenn ich zwei threads mache, damit falls mir jemand mit xml helfen kann er nicht dauernd von diesem abgelenkt wird :oops:

das snippet ist ein bestehender, funktionierender code und ist nicht von mir. der code scheint alle informationen aus der lamedb auszulesen und stellt diese wohl auch lesbar zur verfügung.
denn im zugehörigen hauptcode ( http://python-forum.de/pastebin.php?mode=view&s=16 ) werden alle diese infos auf dem fernseher ausgegeben).
wie du richtig vermutet hast handelt es sich um satellitenreceiver bzw. deren kanalliste.


deinen code wollte ich grade probieren, aber bekomme folgenden fehler:
Traceback (most recent call last):
File "D:\Neuer Ordner\test.py", line 35, in <module>
print("#".join(transponder("ZDF HD")))
File "D:\Neuer Ordner\test.py", line 28, in transponder
return transponders[services[service_name][0].split(":")[2]][1][5:].split(":")
KeyError: 'ZDF HD'
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@mocca: Nun weißt Du, was "ein" sma ist ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mocca
User
Beiträge: 84
Registriert: Mittwoch 4. März 2009, 16:44

:mrgreen: :mrgreen: :mrgreen:

um das nochmal aufzugreifen:
sma hat geschrieben:Hack deswegen, weil doch viele "magische" Konstanten enthalten sind und besser Exemplare von zwei Klassen Service und Transponder erzeugt werden sollten, an deren Attributen man dann ablesen kann, was die Zahlen bedeuten.
ich dachte halt einfach, dass es einfacher ist man liest erstmal alle infos ein und weist diese dann zu, sprich alle transponder-daten zum jeweiligen service, und wenn man diese gruppen dann hat, dann fängt man an zu sortieren, wie man es haben will.

wenn es sinnvoller oder einfach ist dies vorab zu tun, dann wärs natürlich super sozusagen jeden wert als einzelwert gleich zu speichern.

sprich zu jedem service alle durch ":" oder zeilenumbruch getrennten Angaben in jeweils eine eigene Konstante und gleiches für den Transponder. allerdings muss nach wie vor die jeweilige transponderinfo bei dem jeweiligen service bleiben.

sprich, perfekt wäre, wenn der output an ende entweder so vorliegt/abrufbar ist):

Code: Alles auswählen

print (wertA[service],wertB[service],wertC[service],wert1[transponder[service]],wert2[transponder[service]],wert3[transponder[service]],....)
"wertBuchstabe" in diesem Fall sind die daten, die man im bereich "service" für jeden einzelnen sender erhält. deshalb [service], sprich in abhängigkeit vom jeweiligen service (=sender).
"wertZahl" wiederum sind die informationen, die man aus dem bereich "transponder" erhält, die ja aber den jeweiligen sendern zugewiesen werden müssen, deshalb [transponder[service]].

wofür wertABCD und wert 1234 dann jeweils stehen weise ich dann natürlich in sinnvoller beschreibung zu, sprich wertA wäre dann meinetwegen ServiceName und wert1 wäre Frequency. hab ich nur nicht angeführt, weil es sinnvoller so ist.

zu jedem sender gehört exakt ein transponder (aber zu jedem transponder mehrere sender).

alternativ, falls das irgendwie einfacher ist, kann man das ganze auch als xml speichern. falls das irgendwie den code erleichtern würde, damit am ende dann sowas rauskommt

Code: Alles auswählen

  <Service>
    <SatId>1</SatId>
    <Frequency>11361</Frequency>
    <ChannelNo>1</ChannelNo>
    <ServiceName>Das Erste HD</ServiceName>
    <ServiceType>TV</ServiceType>
    <Scrambled>false</Scrambled>
    <Sid>11100</Sid>
    <Oid>1</Oid>
    <Tsid>1011</Tsid>
    <VideoPid>6010</VideoPid>
    <AudioPid>6020</AudioPid>
    <PcrPid>6010</PcrPid>
    <ChildrenLock>false</ChildrenLock>
    <Skip>false</Skip>
    <HD>true</HD>
    <FavoriteNo>1</FavoriteNo>
  </Service>
all diese infos stecken nämlich in diesen ganzen durch ":" getrennten werten drin.

die daten aus einer xml-datei auszulesen kann ich dank eurer hilfe ja mittlerweile ;)
mocca
User
Beiträge: 84
Registriert: Mittwoch 4. März 2009, 16:44

ok, ich ergänze mal die fehlermeldung mit KeyError von meinem vorletzten Post:

Code: Alles auswählen

>>> import os
>>> currDir = os.getcwd()
>>> lamedb_file = open(os.path.join(currDir,'lamedb'),'r')
>>> lamedb_file.read()
'transponders\n0028ad55:0001:ffff\n   s 11605000:2894000:1:3:40:2:0\n/\n00c00000:03f3:0001\n   s 11361750:22000000:0:2:192:2:0:1:2:0:2\n/\n014ab0ea:0000:0001\n   s 12522000:27500000:1:4:330:2:0\n/\nend\nservices\n2b66:00c00000:03f3:0001:25:0\nZDF HD\np:ZDFvision\n2b5c:00c00000:03f3:0001:25:0\nDas Erste HD\np:ARD,c:00177a,c:011784,c:02178e,c:03177a,c:050001\nend'
>>> lamedb_file.readline()
''
>>> 
er liest also problemlos die datei als ganzes aber kann anschliessend nicht die einzelne zeile lesen.
wenn ich die datei aber neu öffne, kann er es

Code: Alles auswählen

>>> lamedb_file.readline()
'transponders\n'
>>> 
hab also mal vor jede for schleife direkt die datei wieder öffnen lassen. dann funktionieren alle print (wenn ich die next alle auskommentiere). denn ich kriege halt noch:

Code: Alles auswählen

NameError: global name 'next' is not defined
hier mal der code (die prints etc. kommen dann raus, wenn der fehler gefunden ist)

Code: Alles auswählen

import os
import sys

transponders = {}
services = {}

def parse_lamedb(itr):
    test = open(os.path.join(currDir,'lamedb'),'r')
    for line in test:
        if line == "transponders":
            print "transponders"
            break
    test = open(os.path.join(currDir,'lamedb'),'r')
    for line in test:
        if line == "end":
            print "end"
            break
        print "endnot1"
        #s = next(test)
        #assert next(test) == "/"
        #transponders[line.split(":")[1]] = (line, s)
    test = open(os.path.join(currDir,'lamedb'),'r')
    for line in test:
        if line == "services":
            print "services"
            break
    test = open(os.path.join(currDir,'lamedb'),'r')
    for line in test:
        if line == "end":
            print "end2"
            break
        print "end2not"
        #line2 = next(test)
        #line3 = next(test)
        #services[line2] = (line, line3)

def transponder(service_name):
    return transponders[services[service_name][0].split(":")[2]][1][5:].split(":")

currDir = os.getcwd()
lamedb_file = open(os.path.join(currDir,'lamedb'),'r')
parse_lamedb(lamedb_file)
#print("#".join(transponder("ZDF HD")))
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

Damit erstellst du eine Liste mit allen Services, die Transponderinformation stehen natürlich auch drinnen. Das Problem ist nur, dass nicht jeder Service einen Transponder hat.

Code: Alles auswählen

transponders = {}
services = {}

with open('actuallamedbsource.txt') as f:
    f.readline()
    print f.readline()
    for line in f:
        if line.strip() == 'end':
            break
        line1 = line.strip().split(':')
        line2 = f.next().strip().split(':')
        transponders[line1[1]] = line1 + line2
##         print line1[1]
        f.next()
            
    print f.next()
    for line in f:
        if line == 'end':
            break
        try:
            line1 = line.strip().split(':')
            line2 = f.next().strip()
            line3 = f.next().strip().split(',')
            if line2 == '':
                continue
            
            services[line2] = (line1 + [line2] + line3 + transponders[line1[3]])
##             print services[line2][6]
        except KeyError as e:
            print e
        except StopIteration:
            break
            
## print services
mocca
User
Beiträge: 84
Registriert: Mittwoch 4. März 2009, 16:44

:oops:
D:\ordner2\test.py:4: Warning: 'with' will become a reserved keyword in Python 2.6
DaMutz hat geschrieben:Damit erstellst du eine Liste mit allen Services, die Transponderinformation stehen natürlich auch drinnen. Das Problem ist nur, dass nicht jeder Service einen Transponder hat.
wie meinst du das? jeder service hat einen zugehörigen transponder. muss ja, denn irgendwo muss der sender (=service) ja gefunden worden sein.


wenn ich deinen code abändere, um die ganzen with und as rauszukriegen

Code: Alles auswählen

import os

transponders = {}
services = {}
currDir = os.getcwd()
f = open(os.path.join(currDir,'lamedb'),'r')
f.readline()
print f.readline()
for line in f:
    if line.strip() == 'end':
        break
    line1 = line.strip().split(':')
    line2 = f.next().strip().split(':')
    transponders[line1[1]] = line1 + line2
    print line1[1]
    f.next()
        
print f.next()
for line in f:
    if line == 'end':
        break
    try:
        line1 = line.strip().split(':')
        line2 = f.next().strip()
        line3 = f.next().strip().split(',')
        if line2 == '':
            continue
        
        services[line2] = (line1 + [line2] + line3 + transponders[line1[3]])
        print services[line2][6]
    except KeyError:
        print e
    except StopIteration:
        break
            
print services
 
dann läufts zwar teilweise, aber es fehlen die meisten angaben und die gruppierungen sind daneben.

Code: Alles auswählen

>>> 
0028ad55:0001:ffff

2894000
22000000
27500000
Traceback (most recent call last):
  File "D:\ordner2\test.py", line 14, in <module>
    transponders[line1[1]] = line1 + line2
IndexError: list index out of range
>>> 
die werte, die da rauskommen sind bei deinem code
- die erste zeile des ersten transponders
- der zweite wert der zweiten zeile aller transponder

ist das so absicht gewesen? oder würde das mit beheben des out of range fehlers behoben werden? denn für die abstimmung mit den services muss der zweite wert der ERSTEN zeile des transponders benutzt werden.
Antworten