Eine txt datei zeilenweise einlesen und vergleichen

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.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

python-fan hat geschrieben:(...) aber erzeugt keine Ausgabe und die Datei beinhaltet I.
Sicher dass keine Leerzeichen vor dem I auftauchen? Das ist bei mir nämlich passiert als ich den Code aus dem Forum kopiert habe. Sonst probier mal meine Variante. Die kann mit Leerzeichen am Anfang umgehen.
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

snafu hat geschrieben:Ich würde wohl auch den Ansatz über groupby() gehen:

Code: Alles auswählen

from itertools import groupby

def parse(stream):
    sensor_id = value = None
    for line in stream:
        msg_type = line.split()[0].strip()
        if msg_type == 'I':
            sensor_id = line.split()[5].split('-')[0]
        if msg_type == 'A':
            value = line.split('"')[1].strip()
        if sensor_id and value:
            yield sensor_id, value
            sensor_id = value = None

def cleanup(records):
    def get_id(record):
        return record[0]
    for sensor_id, sensor_data in groupby(records, get_id):
        last_data = list(sensor_data)[-1]
        yield last_data

def print_records(stream):
    records = parse(stream)
    for sensor_id, value in cleanup(records):
        print('Sensor ID:', sensor_id)
        print('Value:', value)
        print()

def main():
    with open("deine_datei.txt") as stream:
        print_records(stream)

Hi irgendwie mache ich die ganze zeit etwas falsch, sobald ich es über Python Idle 2.7 ausführe habe ich keine Ausgabe, obwohl ich bei der with function den Dateinamen + r für lesen eingefügt habe. Sobald ich das Skript ausführe habe bekomme ich keine Ausgabe, was mache ich falsch?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@python-fan: das r ist nicht nötig, da Lesen der Default ist. Zeig doch mal den exakten Code, den Du auszuführen versuchst.
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

ich wollte den obigen code testweise ausführen, um das Verhalten mir anzuschauen um dies als Lernansatz zu nehmen, ich habe lediglich nur den Dateinamen hinzugefügt.
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

Oh tut mir leid, ich habe vergessen die Funktion main aufzurufen, sorry Leute :(

d.h. ich habe jetzt print main() hinzugefügt. D.h. die anderen funktionen sind mit sich verbunden und müssen nicht explizit aufgerufen werden , oder täusche ich mich?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

python-fan hat geschrieben:d.h. ich habe jetzt print main() hinzugefügt. D.h. die anderen funktionen sind mit sich verbunden und müssen nicht explizit aufgerufen werden , oder täusche ich mich?
Die main()-Funktion ist mehr zu Demozwecken gedacht. Du kannst in dein Programm natürlich direkt den Code aus main() rüberkopieren. Das Aufrufen der Hilfsfunktionen geschieht in print_records(). Deine (Python-)Programierkenntnisse scheinen sich ja in sehr überschaubaren Grenzen zu halten...
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

Ok danke vielmals für die Erklärung. Wenn man in einer anderen Programmiersprache programmiert und eine anderen Syntax gewohnt ist, kann man auch gewisse Sachen übersehen oder man braucht etwas, um es zu verstehen.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier mal mit Index-Operationen, um die "Wegwerf-Listen" zu vermeiden:

Code: Alles auswählen

from __future__ import print_function
from itertools import groupby

def parse(stream):
    sensor_id = value = None
    for line in stream:
        msg_type = line.lstrip()[0]
        if msg_type == 'I':
            start = line.index('from') + 5
            stop = line.index('-', start)
            sensor_id = line[start:stop]
        if msg_type == 'A':
            start = line.index('"') + 1
            stop = line.index('"', start)
            raw = line[start:stop]
            parts = raw.split(',')
            value = tuple(map(int, parts))
        if sensor_id and value:
            yield sensor_id, value
            sensor_id = value = None

def cleanup(records):
    def get_id(record):
        return record[0]
    for sensor_id, sensor_data in groupby(records, get_id):
        for record in sensor_data:
            pass
        yield record

def print_records(stream):
    records = parse(stream)
    for sensor_id, value in cleanup(records):
        print('Sensor ID:', sensor_id)
        print('Value:', value)
        print()

def main():
    with open("deine_datei.csv") as stream:
        print_records(stream)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

python-fan hat geschrieben:Wenn man in einer anderen Programmiersprache programmiert und eine anderen Syntax gewohnt ist, kann man auch gewisse Sachen übersehen oder man braucht etwas, um es zu verstehen.
Das ist schon klar. Aber Funktionen und ihr Sinn, welcher sich normalerweise aus ihrem Namen ergibt, sind dir doch wohl bekannt.

Wenn du an bestimmten Stellen nicht weiterkommst, dann frag ruhig. Die groupby()-Doku hatte ich ja bereits verlinkt. Kannst bei Bedarf aber gerne eine Erklärung dazu auf Deutsch haben. Das tuple-map-Konstrukt, welches ich zum Parsen des Wertes nachträglich editiert hatte, könnte auch nicht jedem geläufig sein...
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

snafu hat geschrieben:Hier mal mit Index-Operationen, um die "Wegwerf-Listen" zu vermeiden:
Nicht Dein Ernst!
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sirius3 hat geschrieben:
snafu hat geschrieben:Hier mal mit Index-Operationen, um die "Wegwerf-Listen" zu vermeiden:
Nicht Dein Ernst!
Du bist ja sehr konstruktiv mit deiner Kritik...

Weiß nicht wo das Problem liegt ob ich jetzt an Anführungszeichen splitte und das zweite Element rausfriemel oder ob ich mir die Start- und Endpunkte mittels index() hole. Ein Gefrickel ist es so oder so. Wirklich robuster ist das Splitten auch nicht. Man muss in beiden Fällen hoffen, dass sich das Format nicht ändert.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: wegen dem Gefrickel ignoriert meine Lösung die A-Zeilen komplett. Eine kurze einzeilige Lösung ist einer mit 2 Hilfsvariablen meist vorzuziehen.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Bei der groupby-Variante missfällt mir, dass von den zurückgegebenen Iteratoren jeweils nur das letzte Element gebraucht wird. Bei nur einem oder zwei Elementen ist das unerheblich, aber bei vielen Einträgen wird dies unschön. In der Hinsicht finde ich die Filter-Lösung besser, die lediglich den zuletzt gelesenen Eintrag chached.

Um den Filter nicht in der Parser-Funktion zu haben, wäre auch ein Dekorator eine mögliche Lösung. Zur Abwechslung hier mal eine klassenbasierte Version (Python 3):

Code: Alles auswählen

class FilterDoubles:

    def __init__(self, func):
        self.func = func
        
    def __call__(self, fobj):
        id = data = None
        for next_id, next_data in self.func(fobj):
            if id and id != next_id:
                yield id, data
            id, data = next_id, next_data
        yield id, data


@FilterDoubles
def get_sensor_data(fobj):
    for line in filter(lambda line: line.startswith('I'), fobj):
        parts = line.split()
        sensor_id = parts[5][:-1]
        data = ''.join(chr(int(c, 16)) for c in parts[-1].split('-') if c != '00')
        yield sensor_id, data


with open(inputfile) as fobj:
    for id, data in get_sensor_data(fobj):
        print(id, data)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Für die Umwandlung der hexadezimalen Zeichen bringt Python übrigens schon selbst was mit:

Code: Alles auswählen

from binascii import unhexlify

# ...
hexstr = line.split()[-1].replace('-', '')
unhexlify(hexstr).strip(b'\00')
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@kbr: soweit ich weiß, cached groupby nichts intern, so dass man es auch für große Listen verwenden kann. Und warum sollte man etwas nachprogrammieren, wenn es es schon fertig gibt.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Sirius3: Habe nochmal in die Doku geschaut und groupby scheint tatsächlich nichts zu cachen, sowie bei dieser Anwendung dem Dekorator ziemlich zu ähneln; der gefällt mir dennoch gut und manchmal kann es eben doch NIH sein ...
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

snafu hat geschrieben:
python-fan hat geschrieben:Wenn man in einer anderen Programmiersprache programmiert und eine anderen Syntax gewohnt ist, kann man auch gewisse Sachen übersehen oder man braucht etwas, um es zu verstehen.
Das ist schon klar. Aber Funktionen und ihr Sinn, welcher sich normalerweise aus ihrem Namen ergibt, sind dir doch wohl bekannt.

Wenn du an bestimmten Stellen nicht weiterkommst, dann frag ruhig. Die groupby()-Doku hatte ich ja bereits verlinkt. Kannst bei Bedarf aber gerne eine Erklärung dazu auf Deutsch haben. Das tuple-map-Konstrukt, welches ich zum Parsen des Wertes nachträglich editiert hatte, könnte auch nicht jedem geläufig sein...

Code: Alles auswählen

if msg_type == 'I':
            start = line.index('from') + 5
            stop = line.index('-', start)
            sensor_id = line[start:stop]
        if msg_type == 'A':
            start = line.index('"') + 1
            stop = line.index('"', start)
            raw = line[start:stop]
            parts = raw.split(',')
            value = tuple(map(int, parts))

D.h. also dass du als index from +5 nimmst d.h. du nimmst aus dem muster from 6e400002 --> 0002 bis zum - ? Verstehe ich das richtig? Damit sparst du dir den ganzen Wert 6e400002 zu nehmen sondern nur ein Teil.

Vielen Dank auch an alle anderen, ich lerne viel neues dazu hier.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@python-fan: deshalb mag ich die Lösung mit »index« nicht, weil sie völlig unverständlich ist. Das erste Index sucht das Wörtchen 'from', also die Position des ›f‹, da muß man noch die 4 Zeichen und das Leerzeichen, also 5 weiter um an der Position der ›6‹ zu landen.
Antworten