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.
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

Hi ich bin neu hier im Forum und möchte gerne eine Textdatei mit mehreren Zeilen auslesen und nur bestimmte Zeilen ausgeben und in ein neues textfile speichern.

Die ein ausschnitt der Datei die Einzulesen ist sieht folgendermaßen aus:
[codebox=text file=Unbenannt.txt]
I 19:33:10.560 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-36-39-32-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.560 "1,874692,1020 " received
I 19:33:10.561 Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 20-20-32-37-31-2C-20-20-31-34-33-2C-20-20-39-37-33-00-00-00
A 19:33:10.561 " 271, 143, 973 " received
I 19:33:10.561 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-36-39-34-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.561 "1,874694,1020 " received
I 19:33:10.597 Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 20-20-32-37-31-2C-20-20-31-34-33-2C-20-20-39-37-33-00-00-00
A 19:33:10.597 " 271, 143, 973 " received
I 19:33:10.598 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-36-39-35-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.598 "1,874695,1020 " received
I 19:33:10.599 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-37-34-31-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.599 "1,874741,1020 " received
I 19:33:10.600 Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 20-20-32-37-35-2C-20-20-31-34-37-2C-20-20-39-37-32-00-00-00
A 19:33:10.600 " 275, 147, 972 " received
I 19:33:10.601 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-37-34-32-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.601 "1,874742,1020 " received
I 19:33:10.646 Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 20-20-32-37-35-2C-20-20-31-34-37-2C-20-20-39-37-32-00-00-00
A 19:33:10.647 " 275, 147, 972 " received
I 19:33:10.649 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-37-38-39-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.649 "1,874789,1020 " received
I 19:33:10.649 Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 20-20-32-37-36-2C-20-20-31-34-35-2C-20-20-39-37-32-00-00-00
A 19:33:10.649 " 276, 145, 972 " received
I 19:33:10.650 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-37-39-31-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.650 "1,874791,1020 " received
I 19:33:10.650 Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 20-20-32-37-36-2C-20-20-31-34-35-2C-20-20-39-37-32-00-00-00
A 19:33:10.651 " 276, 145, 972 " received
I 19:33:10.695 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-37-39-32-2C-31-30-32-30-00-00-00-00-00-00-00
A 19:33:10.695 "1,874792,1020 " received
I 19:33:10.696 Notification received from 6e400002-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 31-2C-38-37-34-38-33-38-2C-31-30-32-31-00-00-00-00-00-00-00
A 19:33:10.696 "1,874838,1021 " received
I 19:33:10.697 Notification received from 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: (0x) 20-20-32-37-35-2C-20-20-31-34-38-2C-20-20-39-37-33-00-00-00
A 19:33:10.697 " 275, 148, 973 " received
[/code]

Wie zu sehen ist sind es zwei verschiedene Zeilen, eine beginnt mit I und die andere mit A. Ich möchte folgendes erreichen, es sollen immer die Werte der I-Zeile ausgegeben werden z.B: 1,874741,1020 ---> ID, time, GesamtSensorWert
276, 145, 972 --> Sensor1,Sensor2,Sensor3
Wie aber bei Zeile 28 bis 30 zu sehen ist, entstehen manchmal einige Unregelmäßigkeiten, wo immer zwei gleiche Zeilen aufeinander folgen:
1,874792,1020 ID, times, GesamtSensorWert
1,874838,1021 ID, times, GesamtSensorWert
Mein Ziel ist es dies zu vermeiden d.h. wie im obigen Beispiel, falls so etwas auftritt, wird die vorherige Zeile(rot) übersrpungen und die zweite Zeile (blau) genommen. Ich habe mit Erkennungspunkten gearbeitet um die Zeilen zu erkennen und anhand der Teil der ID, der sich ändert zum erkennen verwendet (6e400002 und 6e400003). Ich bin soweit, dass er mir das erkennt und einen Kommentat ausgibt, ich will es aber so, dass er die Zeilen miteinander vergleicht und falls doppelt, die erste der doppelten nicht wahrnimmt. Könnt Ihr mir helfen?
Dazu auch mein jetziger Code:

Code: Alles auswählen

csv_file_path = 'C:/Users/Luigi/Downloads/python-csv-parser/S1.txt'

csv_file_contents = open(csv_file_path, "r");

BLE_UUID_prefix_1 ="6e400002"
BLE_UUID_prefix_2 ="6e400003"
prev_prefix = 0
repeat = 0

for line in csv_file_contents:
    
    if line.startswith("I"):
        if BLE_UUID_prefix_1 in line:
            if prev_prefix == BLE_UUID_prefix_1:
                repeat = 1
                prev_prefix = BLE_UUID_prefix_1
                #print prev_prefix    
            else:
                repeat = 0
                prev_prefix = BLE_UUID_prefix_1
                print prev_prefix
                    
        if BLE_UUID_prefix_2 in line:
            if prev_prefix == BLE_UUID_prefix_2:
                repeat = 1
                prev_prefix = BLE_UUID_prefix_2
                #print prev_prefix
            else:
                repeat = 0
                prev_prefix = BLE_UUID_prefix_2
                print prev_prefix    
        #continue  --> continue wird nichr benötigt (returns control to the beginning of while loop
    
    if line.startswith("A"):
        if repeat == 1:
            repeat = 0
            print "skip line"
        if repeat == 0:
            #print line
            #print [pos for pos, char in enumerate(line) if char == '"']
            line_payload = line[16:36]
            line_payload = line_payload.strip()
            print line_payload

Wie könnte ich das realisieren?

Danke vielmals
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Auch wenn du Code und Beispieldaten zeigst (was ja schonmal loeblich ist), werde ich aus deiner Problembeschreibung nicht schlau. Was genau ist dein Kriterium dafuer, das ein Eintrag zum zweiten mal vorkommt?
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

Hi danke vielmals, das sind Sensordaten kabellos übertragen werden auf der App und diese werden geloggt (das ist die Datei mit den Werten). Wie du vielleicht gesehen hast entstehen gewisse Unregelmäßigkeiten in der Übertragung (siehe Muster).Normalerweise muss dies folgendermaßen aussehen:

ID,time,GesamtSensorWert
Sensor1, Sensor2, Sensor3

doch machmal entspricht es nicht diesem muster und es passiert folgendes:
ID,time,GesamtSensorWert
ID,time,GesamtSensorWert

Nun habe ich in Python ein Programm geschrieben, um diese Unregelmäßigkeiten zu elimineren ,d,h. die Datei Zeilenweise einlesen, vergleichen und die editierte Version in eine neue Datei abzuspeichern. Nehmen wir mal an es kommt folgende Situation:
ID,time,GesamtSensorWert (erste Zeile)
ID,time,GesamtSensorWert (zweite Zeile)
Sensor1, Sensor2, Sensor3 (dritte Zeile)

Dann muss das Programm in der Lage sein dies zu erkennen die erste Zeile zu überspringen und ab der zeiten Zeile die Werte zu nehmen,
ist es etwas klarer? -- Mein Ziel ist es immer folgende Zeilenreihenfolge zu haben:
ID,time,GesamtSensorWert
Sensor1, Sensor2, Sensor3
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ok, das klingt besser, anhand deiner Daten kann ich das aber immer noch nicht erkennen - Zeilen 28 - 30 zeigen fuer mein Dafuerhalten dieses Verhalten nicht, und andere sehe ich auch nicht.

Ein Muster, um dein Problem zu loesen koennte sein

Code: Alles auswählen

i_line = None
for line in input_file:
      if line.startswith('I'):
           i_line = line
           continue # erzwingt, das wir beliebig viele I-zeilen lesen
      assert line.startswith('A') # muss stimmen, sonst krachts
      create_output(i_line, line)
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: nein, einer I-Zeile folgt immer eine A-Zeile. Es kommt auf die 64bittige Device-ID an.

@python-fan: Zerlege Dein Problem in kleinere Teile, die für sich einfach zu lösen sind; als erstes das Parsen der Inputdaten, als nächstes das Überspringen doppelter Einträge; das geht z.B. ganz leicht mit itertools.group_by, wenn man den Input schon in der Form (Device-ID, Messwerte) hat. Und als letztes das Schreiben der Ergebnisse im gewünschten Format.
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

@__deets__: Genau Sirius3 hat recht. Das ist was ich meinte ich erkenne es anhand der 64bitigen Device-ID da alles gleich ich an der ID außer die ersten 8 bits (6e400002 oder 6e400003) habe ich diese als Vergleichsmerkal verwendet, um es zuerkennen.
Mein Problem er erkennt es zwar und gibt split line aus,aber die Zeile wird trotzdem ausgegeben, da es davon abhängt ob die Zeile davor ausgegeben wir, ob die darauffolgende nicht die gleiche Device ID hat, d.h --> Zeile 1 mit Device ID : 6e400002 , Zeile 2 mit Device ID : 6e400002, dann soll Zeile 1 übersrprungen werden.

@Sirius3 : Danke für den Tipp, iwie blicke ich gerade nicht mehr durch, könntest du mir eventuell einen Beispiel dazu liefern, wie man so etwas umsetzt? - Sehe den Wald vor lauter Bäumen nicht mehr, da ich viel probiert habe und ich leider da nicht mehr weiter komme :(.

Hier nochmal damit das Problem klarer wird ein Teil des Outputs von meinem aktuellen Programm:
[codebox=text file=Unbenannt.txt]

6e400002
1,874692,1020
6e400003
271, 143, 973
6e400002
1,874694,1020
6e400003
271, 143, 973
6e400002
1,874695,1020
skip line
1,874741,1020
6e400003
275, 147, 972
6e400002
1,874742,1020
6e400003
275, 147, 972
6e400002
1,874789,1020
6e400003
276, 145, 972
6e400002
1,874791,1020
6e400003
276, 145, 972
6e400002
1,874792,1020
skip line
1,874838,1021
6e400003
275, 148, 973

[/code]
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Schreib erstmal ein Programm, welches die beiden Zeilen-Arten auseinander nimmt (also die Werte wie du sie brauchst, ID und Daten). Da sollten tupel mit entsprechend vielen Einträgen rauskommen.

Wenn das steht, setz jeweils eine I und eine A Information zu einer Zeile hintereinander.

Das ist dann schon die halbe Miete. groupby kommt danach.
Benutzeravatar
kbr
User
Beiträge: 1506
Registriert: Mittwoch 15. Oktober 2008, 09:27

Oder statt 'groupby' direkt ein Generator:

Code: Alles auswählen

def get_sensor_data(fobj):
    sensor_id = None
    data = None
    for line in fobj:
        if line.startswith('I'):
            next_id = line.split()[5][:-1]
            if sensor_id and sensor_id != next_id:
                yield sensor_id, data
            sensor_id = next_id
        else:
            data = line.split('"')[1]
    yield sensor_id, data
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

Hi, die Zeilen bzw. Werte habe ich doch bereits getrennt in meinem Programm und gebe diese aus. Diese sind auch schon mit kommagetrennt , da sie so ankommen und ich diese auch so brauche. Oder wie meinst du das?


@ kbr: vielen dank d.h. mit diesem kann ich den Vergleich machen, ab Zeile 7 ist mir das nicht mehr klar, was da genau passiert
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@kbr: am besten schreibt man Funktionen, die genau eine Funktion haben. Das Parsen der Datei ist schon komplex genug, da braucht man nicht auch noch einen Filter mit einbauen.

Code: Alles auswählen

from itertools import groupby, ifilter
from operator import itemgetter

def read_sensors(lines):
    lines = ifilter(lambda l: l.startswith('I'), lines)
    for line in lines:
        parts = line.split()
        device_id = parts[5].strip(',')
        data = ''.join(chr(int(c,16)) for c in parts[-1].split('-'))
        yield device_id, data.rstrip('\0')

def last(iterable):
    for item in iterable:
        pass
    return item

with open(filename) as lines:
    for _, data in groupby(read_sensors(lines), itemgetter(0)):
        device_id, data = last(data)
        print device_id, data
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

Hi Sirius danke ebenfalls, ich habe es ausgeführt um, die Funktionsweise besser zu verstehen, da ich hier aus dem Forum auch etas lernen möchte. Ich habe den path für die Datei zum öffnen auf meine Bedürfnisse angepasst, erhalte aber keine Ausgabe :(. Der letzte Teil ist doch für das Öffnen der zu bearbeitenden Datei , mach ich da etwas falsch?
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@python-fan: Du mußt nur der Variable »filename« den richtigen Wert geben. Keine Ausgabe kann es eigentlich nur geben, wenn in der Datei keine Zeile mit 'I' anfängt.
python-fan
User
Beiträge: 11
Registriert: Donnerstag 4. Januar 2018, 14:38

@ Sirius, ja das habe ich gemacht aber erzeugt keine Ausgabe und die Datei beinhaltet I.

So habe ich es angegeben:

Code: Alles auswählen


csv_file_path = 'C:/Users/py/python-csv-parser/S1.txt'



 with open(csv_file_path, "r") as lines:
        for _, data in groupby(read_sensors(lines), itemgetter(0)):
            device_id, data = last(data)
            print device_id, data


Benutzeravatar
kbr
User
Beiträge: 1506
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Sirius3: Es ist sicher eine gute Idee, die Daten direkt aus der Sensorzeile zu entnehmen und nicht aus dem nachfolgenden A-Record, auch wenn der Code dadurch etwas komplexer ausschaut. Ob man bei der gegebenen Komplexität der Daten groupby nutzt oder einen Filter verwendet, nun ja ... aus pragmatischer Sicht würde ich den Filter nehmen, aus akademischer Sicht die groupby-Variante.
Dein Beispiel wird für den TE aber auch nicht leichter zu verstehen sein.
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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)
Benutzeravatar
snafu
User
Beiträge: 6854
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: 18260
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?
Antworten