log parsen REGEX Zeilen SQLITE übergeben

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
wannabe
User
Beiträge: 7
Registriert: Dienstag 30. November 2010, 13:27

Hallo Allerseits,

habe mich bislang geziert hier Fragen zu stellen, jetzt drückt es aber doch zu sehr, da ich nicht weiß wo anzusetzen ist:
Problem: ich parse ein Logfile und kann erfolgreich 3 wichtige Werte extrahieren, diese werden mittels print value als 3 Zeilen ausgespuckt:
06026104
0.1501
02484.796

Ich möchte diese Werte in SQLite Tabelle mit 4 Werten übergeben, der erste Wert ist die ID als prim.Key, die anderen 3 SPalten sollen gefüllt werden mit obigen Werten. SQLite-Fehlermeldung, die Tabelle hat ja 4 Spalten (eine ID, die soll doch aber SQLite selbst führen)

Nun weiß ich nicht, ob ich im die Werte nochmal vorbereiten muß, oder ob ich bei der SQLite-Eintragung ansetzen soll? Bin blutigster Anfänger und sehe mitunter Möglichkeiten nicht.
Hat jemand eine Idee?

Code: Alles auswählen

with open("verbrauch.log") as fp:
	for line in fp:
		match = re.search(r'(0\.0\.0|1\.6\.1|1\.8\.1)\(([0-9\.]+)', line)
		if not match: continue
		version, value = match.groups() #version ist zu vernachlässigen
		#print value würde Ausgabe wie oben bringen

for item in value:
	cursor.executemany("INSERT INTO tabellename values (?,?,?)", item)

#	cursor.execute('INSERT INTO tabellename(sernr, peak, verb) values (?,?,?)', item)

#	cursor.execute("INSERT INTO tabellename(sernr) values (?)",(item[0],));
#	cursor.execute("INSERT INTO tabellename(peak) values (?)",(item[1],));
#	cursor.execute("INSERT INTO tabellename(verb) values (?)",(item[2],));

fp.close()
conn.commit()
cursor.close()
conn.close()
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

Code: Alles auswählen

cursor.execute('INSERT INTO tabellename(sernr, peak, verb) values (?,?,?)', item)
dies sollte funktionieren, wenn du einen Leerschlag zwischen tabellename und Klammer setzt.

Weitere Punkte:
- fp.close() ist nicht nötig, da die Datei bereits geschlossen ist.

für diese Zeile:

Code: Alles auswählen

version, value = match.groups() #version ist zu vernachlässigen
schreib doch lieber:

Code: Alles auswählen

value = match.groups()[1] #1. Spalte ist zu vernachlässigen
oder lösche die Klammer in der RegEx.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

DaMutz hat geschrieben:für diese Zeile:

Code: Alles auswählen

version, value = match.groups() #version ist zu vernachlässigen
schreib doch lieber:

Code: Alles auswählen

value = match.groups()[1] #1. Spalte ist zu vernachlässigen
Ob das nun wirklich besser ist ist, wage ich mal stark zu bezweifeln...
wannabe
User
Beiträge: 7
Registriert: Dienstag 30. November 2010, 13:27

ich hatte ja bereits die eigentliche Ausgabe notiert:
06026104
0.1501
02484.796
Die ursprüngliche Eingabe sah so aus, nur so am Rande:
1.8.1(02484.825)
1.8.1*32(02449.574)
1.8.1*31(02393.949)
1.8.1*30(02341.038)

trotz des Leerzeichenhinweises (danke, sowas sollte nicht passieren), bleibt es bei SQL-Fehl.meld. Incorrect number of bindings supplied. The current statement uses 3, and there are 1 supplied Nur 1 Wert? Wie eigenartig.
ich habe einfach mal versucht die obige Ausgabe in einer Liste zu sortieren, mit selben Ergebnis.

Code: Alles auswählen

version, value = match.groups()
		mylist.append(value)

for item in mylist:
Die Fehlermeldung ändert sich wenn ich die Spaltenbezeichnung weglasse:

Code: Alles auswählen

cursor.executemany('INSERT INTO energielog values (?,?,?)', item)
OperationalError: table energielog has 4 columns but 3 values were supplied

Da scheint mir die Übergabe zumindest zu klappen. Ich elaboriere jedoch an der sicher trivialen Frage, wie ist das mit der ID in SQLite-Tabelle? diesen fortlaufenden Schlüssel übergebe ich ja nicht, denn beim nächsten Durchlaufen des log-Parsens sind es andere Werte; .schema ist energielog (ID INTEGER PRIMARY KEY, kwh NUMERIC, peak NUMERIC, sernr NUMERIC)

Ihr seht, es ist durchaus verzwickt und ich weiß einfach nicht wo anzusetzen wäre. Es muß irgendwas mit dieser Listung auf sich haben.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Wenn ich mich richtig erinnere, sollte es so funktionieren:

Code: Alles auswählen

cursor.executemany('INSERT INTO energielog values (?,?,?,?)', (None, x, y, z))
Die erste Spalte muss dabei als Primary Key gesetzt sein.

So spart man sich den überflüssigen Namen und den Index:

Code: Alles auswählen

_, value = match.groups()
Das Leben ist wie ein Tennisball.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@wannabe: Hast du noch den Befehl, mit dem die die Tabelle in der SQLite DB angelegt hast? Ich tippe im Moment mal drauf, dass du für die 1. Spalte kein "auto increment" angelegt hast.

Ansonsten kann du dir das auch via Kommandozeile anzeigen lassen
  • DB-Datei mit SQLite(3) öffnen
  • .schema Name_der_Tabelle
Gruß, noisefloor
wannabe
User
Beiträge: 7
Registriert: Dienstag 30. November 2010, 13:27

schlicht wie ich bin, habe ich mir den sqlitebrowser gegriffen zum Erstellen. Dort gibt es Autoincrement gar nicht, überhaupt scheint es bei SQLite durch ROWID ersetzt zu sein
http://www.indigorose.com/forums/thread ... ith-SQLITE

Scheinbar muß man einfach nur für Autoincrement, wie EyDu vorschlug, Null mitgeben. Jedoch bringt er cursor.executemany('INSERT INTO energielog values (?,?,?,?)', (None, x, y, z))
NameError: name 'x' is not defined


was sollte bei .. x, y, z) eingetragen werden?

Danke noisefloor, wie bemerkt, scheinbar muß man kein auto increment anlegen (kann das überhaupt sein?)
.schema-Abfrage: CREATE TABLE energielog (ID INTEGER PRIMARY KEY, kwh NUMERIC, peak NUMERIC, sernr NUMERIC);

AkteX sag ich da nur.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Na die drei Wert die bei dir in "item" stehen. Da sollte man aber drauf kommen.

Oder:

Code: Alles auswählen

 ... , ((None,) + item)
Edit: Angst vor Prügel :D
Zuletzt geändert von EyDu am Donnerstag 2. Dezember 2010, 23:38, insgesamt 1-mal geändert.
Das Leben ist wie ein Tennisball.
BlackJack

@EyDu: Könntest Du bitte ganz schnell das Prozentzeichen da weg editieren. Sonst gibt's Haue. :-)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

BlackJack hat geschrieben:@EyDu: Könntest Du bitte ganz schnell das Prozentzeichen da weg editieren. Sonst gibt's Haue. :-)
Da war wohl noch jemand im String-Formatting-Modus :roll:
Das Leben ist wie ein Tennisball.
wannabe
User
Beiträge: 7
Registriert: Dienstag 30. November 2010, 13:27

Moin,

leider hat das nicht geklappt:

Code: Alles auswählen

cursor.executemany('INSERT INTO energielog values (?,?,?,?)', ((None,) +i))
None wird als string gewertet
TypeError: can only concatenate tuple (not "str") to tuple

mache ich es so:

Code: Alles auswählen

cursor.execute('INSERT INTO energielog(sernr) values (?)', i)
	cursor.execute('INSERT INTO energielog(peak) values (?)', i)
	cursor.execute('INSERT INTO energielog(kwh) values (?)', i)
wird immerhin klaglos etwas eingetragen, leider von oben nach unten: mehrfach der letzte Wert als Zahlenfolge, verteilt über Zeilen.
Ich habe noch vieles andere ausprobiert, ich vermute die Liste der obigen Werte ist eher eine Art Tabelle, jedes Zeichen ist ein item - vorhin hatte ich auch eine Fehlermeldung wo 8 Values gemeldet wurden für 4 Spalten, 06026104 ist bspw. 8 Zeichen lang.
Ich geh mal schlafne, vl. kommt morgen die Eingebung:

Code: Alles auswählen

cursor.executemany('INSERT INTO energielog(sernr,peak,kwh) values (?,?,?)', i)
so schert sich immerhin Python nicht um die ID, aber i bzw. item stimmt halt nicht
BlackJack

@wannabe: Lies mal die Dokumentation was der Unterschied zwischen `execute()` und `executemany()` ist. Insbesondere was die jeweils als zweites Argument erwarten und damit dann machen.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

nochmal zum Thema "autoincrement": SQLite kann das auf zwei Arten. Einmal wie du es jetzt machst (machen musst) oder man nimmt beim Anlegen der Tabelle "AUTOINCREMENT" Schlüsselwert dazu:

http://www.sqlite.org/autoinc.html
http://www.sqlite.org/faq.html

Gruß, noisefloor
wannabe
User
Beiträge: 7
Registriert: Dienstag 30. November 2010, 13:27

Hallo,

ich denke hier liegt ein Missverständnis vor. Wie ich bereits mutmaßte, ist die Ausgabe selbst problematisch, daher ist das Einsortieren in SQLite so schwierig.
06026104
0.1501
02484.796
Ich vermag es nicht, daß Python diesen als 3 konsekutive Zeilen liest, dann wäre das Füttern mit

Code: Alles auswählen

		if not match: continue
		version,value = match.groups()
		mylist.append(value)
for item in mylist:
	t = (item)
	cursor.execute('INSERT INTO energielog(sernr) values (?)', [t])
	cursor.execute('INSERT INTO energielog(peak) values (?)', [t])
	cursor.execute('INSERT INTO energielog(kwh) values (?)', [t])
sicher auch kein Problem mehr. Immerhin werden nun die Werte reingeschreiben, leider Zeile a in Spalte1, Zeile 2 - Spalte 2 usf.
Irgendwie muß ich Python beibiegen, jede Zeile als einen Wert einzuspeisen.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

hm? Hast du dir den mittels print mal ausgeben lassen, wie "item" bzw. "value" aussieht?

Gruß, noisefloor
wannabe
User
Beiträge: 7
Registriert: Dienstag 30. November 2010, 13:27

hab ich, danke fürs Antworten

Code: Alles auswählen

if not match: continue
		version,value = match.groups() #[1]
		print version,value
0.0.0 06026104
1.6.1 0.1501
1.8.1 02484.796

Code: Alles auswählen

version,value = match.groups() #[1]
		print value
06026104
0.1501
02484.796
(die eigentlichen Werte)

Code: Alles auswählen

version,value = match.groups() #[1]
		mylist.append(value)

for item in mylist:
	t = (item)
	print t[0:3]
bringt:
060
0.1
024
Python hat die Werte in Tabelle organisiert und statt Zeilen werden Werte in Spalten gewertet?

Übrigens funktioniert in meinem heutigen Beitrag zumindest die Zuweisung der Ergebnisse in SQLITE in ganzen "Strings", aber eben nicht korrekt.
Wert 1: 06026104
steht so in Zeile1 Spalte1 dann aber wieder in Zeile2 Spalte2 und Zeile3 Spalte3
Wert 2: 0.1501
taucht erstes mal in Zeile4 Spalte1 dann in Zeile5 Spalte2 wieder usf.

Logisch, daß geünscht ist, Wert1 in Spalte namens "Sernr"; Wert2 in Spalte "peak" und so weiter eingetragen wird.
BlackJack

@wannabe: Wenn die drei Einträge in `mylist` eine Zeile in der Datenbank ergeben sollen, dann verstehe ich nicht warum Du versuchst die in einer Schleife einzeln in die DB zu schreiben und dann auch noch nacheinander in alle drei Datenspalten!? Das ist doch total unsinnig!?

Die Bedingung mit dem `match` davor würde ich übrigens umdrehen. Also nicht testen ob kein Treffer und dann ``continue``, sondern testen ob Treffer und dann den Wert an die Liste anhängen. Wobei `mylist` kein guter Name ist.

Das ``t = (item)`` ist sinnfrei. Sowohl die Zuweisung an sich von einem nichtssagenden Namen an einen anderen nichtssagenden Namen, als auch die Klammern.

Wenn Du *einen* Datensatz eintragen willst, dann mach das mit *einem* ``INSERT``. Darin so viele Platzhalter wie Werte in die Zeile eingetragen werden sollen, und als Argument dann eine Liste mit so vielen Werten wie Platzhalter da sind.
wannabe
User
Beiträge: 7
Registriert: Dienstag 30. November 2010, 13:27

Hier das Gesamtskript:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*- 

import serial
import time
import re
import sqlite3

# serielle USB-Schnittstelle öffnen
ser = serial.Serial()
ser.baudrate = 300
ser.port = /dev/ttyUSB0    
ser.timeout = 2
ser.parity = serial.PARITY_EVEN
ser.stopbits = serial.STOPBITS_ONE
ser.bytesize = serial.SEVENBITS
ser.open()

# Zählerabfrage initiieren mit /?!
ser.write("/?!")
time.sleep(0.001)                    #1ms war im cutecom eingestellt und hat funktioniert
ser.write("\r")                    #CR LF?

# sqlite Einbindung vorbereiten
connection = sqlite3.connect(w"/var/httpdocs/dabaPyTest.s3db")
cursor = connection.cursor()
extrakt = []

#theoretisch sollte nun Zähler antworten
ser.readlines(eol='!') = z   
for line in z:
		match = re.search(r'(0\.0\.0|1\.6\.1|1\.8\.1)\(([0-9\.]+)', line)
		if match: 
			version,value = match.groups() 
			extrakt.append(value)
#print extrakt
cursor.execute('INSERT INTO energielog (sernr, peak, kwh) values (?, ?, ?)', extrakt)

ser.close()
cursor.close()
connection.close()
so klappt zumindest des Parsen des Logfiles und Einsortieren in SQLite.

Vielen, vielen Dank für die Hilfe und Hinweise, auch wenn ich mich mitunter sehr angestellt habe. Waren es doch meine ersten Python-Gehversuche.
(code verbessert - Dank für die Tips - hab ich weitere syntaxfehler übersehen? - Ich bin mir vor allem unsicher was ser.readlines anbelangt, obgleich ich vom Gefühl her meine es klappt so)
Zuletzt geändert von wannabe am Dienstag 7. Dezember 2010, 10:40, insgesamt 1-mal geändert.
BlackJack

@wannabe: Öhm, das enthält mindestens drei Syntaxfehler, lässt sich also nicht übersetzen.

Die Objekte würde ich enger "gruppieren", also nicht ganz am Anfang schon die Verbindung zur Datenbank aufbauen und `extrakt` definieren wo das doch gegen Ende erst benötigt wird.

Die Attribute von `serial` kann man auch dem Konstruktoraufruf schon mitgeben.

Die 1.0 (Sekunden) passen nicht zum Kommentar (Millisekunden).

Wofür steht `fp`? Ist zumindest in C eine übliche Abkürzung für "file pointer" -- das passt semantisch nicht zu einer Liste mit Zeichenketten.

`cursor` und `connection` werden in der falschen Reihenfolge geschlossen.
Antworten