zeile filtern zwischen x und y

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
realdaveger
User
Beiträge: 3
Registriert: Freitag 6. März 2015, 10:51

Hallo liebe gemeinde,

bisher habe ich es geschafft alles selbst zu lösen, allerdings habe ich einen, bzw. zwei (dazu evtl. später) Punkt wo ich gerade keinen richtigen Ansatz finde.

Ich möchte eine Variable filtern die zwischen 2 festen werten steht, hier ein Bsp.:

Code: Alles auswählen

1) Jamestoon < Du Poitou > BEST: 2:38:634 TOTAL: 58:32:657 Laps:2 SesID:12
2) Lλ-Tﻩﻩned <LeS.éGoRgEuRs> BEST: 2:44:124 TOTAL: 58:38:334 Laps:2 SesID:11
3) ChuckΨ <LeS.éGoRgEuRs> BEST: 2:46:689 TOTAL: 58:46:653 Laps:2 SesID:6
4) Masciale BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:7
5) ChuckΨ <LeS.éGoRgEuRs> BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:2
6) Lλ-Tﻩﻩned <LeS.éGoRgEuRs> BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:1
7) Poek1979 BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:13
8) Jamestoon < Du Poitou > BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:0
9) Lλ-Tﻩﻩned <LeS.éGoRgEuRs> BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:14
10) lexnachal BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:9
11) Macks Gimpel BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:4
12) Petr Smo BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:5
13) Macks Gimpel BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:10
14) Spookunderdog BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:8
15) lexa130 BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:3
Der Name soll als Variable gespeichert werden (zwischen ") " und "BEST: ") wie man erkennen kann, können so ziehmlich alle zeichen enthalten sein. Eine Lösung wüsste ich, welche allerdings nicht sonderlich gut ist. Im Einsatz habe ich eine Batch welche das ganze löst, "on the dirty way".

Code: Alles auswählen

for /f "tokens=1-10" %%a in (%templog%) do (
	if %%b==BEST: (
	set driver="%%a"
	set "driver=!driver:|=!"
	set "driver=!driver::=!"
	set "driver=!driver:<=!"
	set "driver=!driver:>=!"
	set "driver=!driver:&=!"
	set "driver=!driver:^=!"
	set "driver=!driver:"=!"
	set "driver=!driver:?=!"
	set "driver=!driver:/=!"
	set "driver=!driver:\=!"
	set "driver=!driver:)=!"
	set "driver=!driver:(=!"
	echo "!driver!" "%%c" "%%g" >> %finallog%
	)
	if %%c==BEST: (
	set driver="%%a %%b"
	set "driver=!driver:|=!"
	set "driver=!driver::=!"
	set "driver=!driver:<=!"
	set "driver=!driver:>=!"
	set "driver=!driver:&=!"
	set "driver=!driver:^=!"
	set "driver=!driver:"=!"
	set "driver=!driver:?=!"
	set "driver=!driver:/=!"
	set "driver=!driver:\=!"
	set "driver=!driver:)=!"
	set "driver=!driver:(=!"
	echo "!driver!" "%%d" "%%h" >> %finallog%
	)
	if %%d==BEST: (
	set driver="%%a %%b %%c"
	set "driver=!driver:|=!"
	set "driver=!driver::=!"
	set "driver=!driver:<=!"
	set "driver=!driver:>=!"
	set "driver=!driver:&=!"
	set "driver=!driver:^=!"
	set "driver=!driver:"=!"
	set "driver=!driver:?=!"
	set "driver=!driver:/=!"
	set "driver=!driver:\=!"
	set "driver=!driver:)=!"
	set "driver=!driver:(=!"
	echo "!driver!" "%%e" "%%i" >> %finallog%
	)
	if %%e==BEST: (
	set driver="%%a %%b %%c"
	set "driver=!driver:|=!"
	set "driver=!driver::=!"
	set "driver=!driver:<=!"
	set "driver=!driver:>=!"
	set "driver=!driver:&=!"
	set "driver=!driver:^=!"
	set "driver=!driver:"=!"
	set "driver=!driver:?=!"
	set "driver=!driver:/=!"
	set "driver=!driver:\=!"
	set "driver=!driver:)=!"
	set "driver=!driver:(=!"
	echo "!driver!" "%%f" "%%j" >> %finallog%
	)
)
Das tut auch erst einmal, jedenfalls bis max. 4 Leerzeichen. Das gleiche z.B. mit "split" in Python zu machen wäre meines erachtens schwachsinn, da es sicherlich eine Elegantere lösung gibt.


Danke schon einmal für anregungen und Unterstützung.

Gruß Dave
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Wenn ich deine Frage richtig verstehe, wären hier wohl reguläre Ausdrücke das Werkzeug der Wahl.

Geht vmtl. noch schicker (ich weiß nicht, als wie "elegant" Look-around Assertions angesehen werden), aber so als erster Vorschlag:

Code: Alles auswählen

import re
x = re.compile(r'(?<=\d\) ).+(?= BEST:)')

data = '''1) Jamestoon < Du Poitou > BEST: 2:38:634 TOTAL: 58:32:657 Laps:2 SesID:12
2) Lλ-Tﻩﻩned <LeS.éGoRgEuRs> BEST: 2:44:124 TOTAL: 58:38:334 Laps:2 SesID:11
3) ChuckΨ <LeS.éGoRgEuRs> BEST: 2:46:689 TOTAL: 58:46:653 Laps:2 SesID:6
4) Masciale BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:7
5) ChuckΨ <LeS.éGoRgEuRs> BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:2
6) Lλ-Tﻩﻩned <LeS.éGoRgEuRs> BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:1
7) Poek1979 BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:13
8) Jamestoon < Du Poitou > BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:0
9) Lλ-Tﻩﻩned <LeS.éGoRgEuRs> BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:14
10) lexnachal BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:9
11) Macks Gimpel BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:4
12) Petr Smo BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:5
13) Macks Gimpel BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:10
14) Spookunderdog BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:8
15) lexa130 BEST: 16666:39:999 TOTAL: 0:00:000 Laps:0 SesID:3'''

for line in data.split('\n'):
    print(re.findall(x, line))

Zuletzt geändert von nezzcarth am Freitag 6. März 2015, 11:54, insgesamt 2-mal geändert.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Willkommen im Forum!

Das ist eine Aufgabe fuer RE.

Hier mal eine RE, die deine Datensaetze komplett zerlegt:

Code: Alles auswählen

In [19]: import re

In [20]: s = """1) Jamestoon < Du Poitou > BEST: 2:38:634 TOTAL: 58:32:657 Laps:2 SesID:12"""

In [21]: r = r"(?P<rank>\d+)\) (?P<name>.*?) BEST: (?P<besttime>[0-9:]+) TOTAL: (?P<totaltime>[0-9:]+) Laps:(?P<lapcount>\d+) SesID:(?P<sessionid>\d+)"

In [22]: m = re.match(r, s)

In [23]: m.groupdict()
Out[23]: 
{'besttime': '2:38:634',
 'lapcount': '2',
 'name': 'Jamestoon < Du Poitou >',
 'rank': '1',
 'sessionid': '12',
 'totaltime': '58:32:657'}
realdaveger
User
Beiträge: 3
Registriert: Freitag 6. März 2015, 10:51

super, danke euch. Mit ein bisschen testen und nachlesen, konnte ich meine 2. Frage auch schon selbst erledigen.

Hier meine Lösung:

Code: Alles auswählen

for zeile2 in olog3:
    if "BEST:" in zeile2 and not "16666:39:999" in zeile2:
        r = r"(?P<rank>\d+)\) (?P<name>.*?) BEST: (?P<besttime>[0-9:]+) TOTAL: (?P<totaltime>[0-9:]+) Laps:(?P<lapcount>\d+) SesID:(?P<sessionid>\d+)"
        m = re.match(r, zeile2)
        m1 = m.group(2)
        m2 = m.group(3)
        m3 = m.group(6)
        m1=("".join(re.findall(r"[A-Za-z0-9üäöÜÄÖß ]",m1))).replace("","")
        ofilterlog.write(m1+"|"+m2+"|"+m3+"\n")

Ich habe jetzt allerdings, ein neues Phänomen. Wenn dies eher in einem neuen Thema soll dann kann ich das noch machen.

Wenn ich meinen SQLite Befehl ausführe, dann funktionieren auch die folgenden Befehle:

Code: Alles auswählen

import sqlite3
import csv

conn = sqlite3.connect('test.db')
conn.execute('create table IF NOT EXISTS bestzeiten(id integer primary key autoincrement, track text, car text, name text, time integer);')
conn.execute('DROP TABLE IF EXISTS temp;')
conn.execute('create table temp(track text, name text, time integer, car text);')

csvfile = open('output.csv', 'rt', encoding="utf8")
creader = csv.reader(csvfile, delimiter='|')
for t in creader:
    conn.execute('INSERT INTO temp values (?, ?, ?, ?);', t)
conn.execute('insert into bestzeiten(track, car, name, time) select track, car, name, time from temp;')
conn.execute('DROP TABLE temp;')
conn.commit
Die dann folgenden 2 befehle, welche zum sortieren bzw. Löschen zuständig sind werden nicht richtig in sql ausgeführt.

Code: Alles auswählen

conn.execute("delete from bestzeiten where exists (select id from bestzeiten dup where bestzeiten.track = dup.track and bestzeiten.car = dup.car and bestzeiten.name = dup.name and bestzeiten.time > dup.time);")
conn.commit
conn.execute("delete from bestzeiten where exists (select id from bestzeiten dup where bestzeiten.track = dup.track and bestzeiten.car = dup.car and bestzeiten.name = dup.name and bestzeiten.id > dup.id);")
conn.commit
csvfile.close()
conn.close()
Die befehle über batch funktionieren.

Weiß da jemand weiter?

Danke, Gruß Dave
BlackJack

@realdaveger: Du führst kein einziges `commit()` aus. Du musst die Methode auch *aufrufen*.

Die Namen im ersten Quelltext sind unschön. Man nummeriert keine Namen durch. Dann will man in der Regel eine Datenstruktur verwenden, oder hier habe ich eher den verdacht es wird zu viel in einer Funktion gemacht das man anfangen muss sich Namen mit Nummernanhängseln auszudenken. Und einbuchstabige Namen mit Nummern sind sehr nichtssagend. Man muss auch nicht jeden Pups an Zwischenergebnis an einen eigenen Namen binden. Zum Beispiel eine literale Zeichenkette an den Namen `r` binden und die einzige Stelle wo der Name dann benutzt wird ist der Funktionsaufruf in der direkt folgenden Zeile muss ja nicht sein.

Die Gruppen in dem regulären Ausdruck haben alle Namen — dann sollte man die auch benutzen statt der Indizes wo man mühsam abzählen muss welche Gruppe damit eigentlich gemeint ist.

Das entfernen von Zeichen beim ersten Feld halte ich für problematisch wenn man sich die folgenden SQL-Statements anschaut und davon ausgeht das der Wert vielleicht eindeutig sein muss. `re.sub()` wäre da auch sinnvoller wenn man sowieso schon `re` verwendet.

Ein `replace()` mit zweimal der leeren Zeichenkette als Argumente ist sinnfrei.

Warum wird zum Schreiben der Daten das `csv`-Modul *nicht* verwendet, zum Lesen dann aber schon‽

Wozu wird die temporäre Tabelle verwendet? Und warum wird beim Einfügen nicht schon getestet ob der Eintrag „besser” ist als ein eventuell vorhandender statt hinterher mit ``DELETE`` wieder umständlich Einträge zu entfernen.
realdaveger
User
Beiträge: 3
Registriert: Freitag 6. März 2015, 10:51

Danke dir, commit ist natürlich klar, Funktioniert jetzt auch, danke!

Zu den RE:

replace() wurde gelöscht, war noch ein überbleibsel, ist natürlich sinnfrei gewesen.

Das erste Argument (der Name) das reicht vollkommen für die spätere Anzeige in HTML.
Zum Aufruf über die Namen werde ich mich belesen, das war jetzt die Methode welche für mich leicht ist und Verständlich.
zum Rest muss ich natürlich sagen, bin absoluter python anfänger und versuche gerade meine scripte zu übersetzen, übungen zum anfassen quasi.
Datenbanken sind auch nicht mein Tagesgeschäft, ich werde aber sehen das ich das optimieren kann. Habe hierzu ein einsteigerheftchen für 7 DM neben mir liegen.

so viel zu den Hintergründen :)

Danke nochmal an alle!
BlackJack

@realdaveger: Das mit dem Namen und HTML habe ich nicht verstanden‽ Du filterst aus den Namen im Logfile alles mögliche heraus, so dass am Ende beispielsweise 'BlackJack' und '-=>Black♥Jack<=-' der gleiche Name wären, was ja aber wohl nicht stimmt und sicher nicht gewünscht ist das die Bestzeiten für diese beiden zusammengeworfen werden.

Auf Funktionen aufgeteilt und ungetestet:

Code: Alles auswählen

import csv
import re

CSV_DELIMITER = '|'
LOG_LINE_RE = re.compile(
    r'(?P<rank>\d+)\) (?P<name>.*?)'
    r' BEST: (?P<best_time>[0-9:]+)'
    r' TOTAL: (?P<total_time>[0-9:]+)'
    r' Laps:(?P<lap_count>\d+)'
    r' SesID:(?P<session_id>\d+)'
)


def parse_log(lines):
    return (m.groupdict() for m in map(LOG_LINE_RE.match, lines) if m)


def write_csv(out_file, entries, delimiter=CSV_DELIMITER):
    writer = csv.writer(out_file, delimiter=delimiter)
    writer.writerows(
        (e['name'], e['best_time'], e['session_id'])
        for e in entries
        if e['best_time'] != '16666:39:999'
    )


def main():
    with open('ranking.txt') as lines:
        with open('best_times.csv', 'w') as csv_file:
            write_csv(csv_file, parse_log(lines))


if __name__ == '__main__':
    main()
BlackJack

Ebenfalls ungetestet das eintragen in die Datenbank ohne temporäre Tabelle, das ständige verbrauchen von IDs, und den zwischenzeitlich ”illegalen” Zustand der Tabelle durch mehr als einem Datensatz für eine (track, car, name)-Kombination:

Code: Alles auswählen

import csv
import sqlite3

CSV_DELIMITER = '|'


def main():
    connection = sqlite3.connect('test.db')
    cursor = connection.cursor()
    cursor.execute(
        'CREATE TABLE IF NOT EXISTS bestzeit ('
        ' id INTEGER PRIMARY KEY,'
        ' track TEXT,'
        ' car TEXT,'
        ' name TEXT,'
        ' time INTEGER,'
        ' CONSTRAINT c_bestzeit_1 UNIQUE (track, car, name)'
        ')'
    )

    with open('test.csv') as csv_file:
        reader = csv.reader(csv_file, delimiter=CSV_DELIMITER)
        for track, car, name, new_time in reader:
            new_time = int(new_time)
            cursor.execute(
                'SELECT id, time'
                '  FROM bestzeit'
                ' WHERE track=? AND car=? and name=?',
                (track, car, name)
            )
            row = cursor.fetchone()
            if row:
                id, old_time = row
                if new_time < old_time:
                    cursor.execute(
                        'UPDATE bestzeit SET time=? WHERE id=?', (new_time, id)
                    )
            else:
                cursor.execute(
                    'INSERT INTO bestzeit (track, car, name, time)'
                    ' VALUES (?, ?, ?, ?)',
                    (track, car, name, new_time)
                )
            connection.commit()


if __name__ == '__main__':
    main()
Wobei der DB-Entwurf ein wenig fragwürdig ist da in drei Spalten (track, car, name) redundante Daten stehen wo man eher Fremdschlüssel in andere Tabellen speichern würde.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Um Race-Conditions entgenzuwirken habe ich mir angewöhnt, den INSERT zu probieren und bei Fehlern ein UPDATE zu machen. Ebenso ungetestet:

Code: Alles auswählen

[...]
        for track, car, name, new_time in reader:
            new_time = int(new_time)
            try:
                cursor.execute(
                    'INSERT INTO bestzeit (track, car, name, time)'
                    ' VALUES (?, ?, ?, ?)',
                    (track, car, name, new_time)
                )
            except sqlite3.IntegrityError:
                connection.rollback()
                cursor.execute(
                    'UPDATE bestzeit SET time=?'
                    ' WHERE track=? AND car=? and name=? and time>?',
                    (new_time, track, car, name, new_time)
                )
            connection.commit()
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Sirius3 hat geschrieben:Um Race-Conditions entgenzuwirken habe ich mir angewöhnt, den INSERT zu probieren und bei Fehlern ein UPDATE zu machen. Ebenso ungetestet:
Und wenn zwischen dem ``INSERT`` und dem ``UPDATE`` ein ``DELETE`` stattgefunden hat :twisted:

Ist es nicht so, dass verschiedene RDMS dortauf eigene Weise etwas wie ein "insert or update" Mechanismus anbieten? Das ganze müsste ja eigentlich atomar ablaufen, ansonsten hat man keine absolute Garantie...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Hyperion: Wenn das DELETE in einer anderen Transaktion stattgefunden hat, dann muss es in der Aktuellen ja nicht sichtbar sein.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:@Hyperion: Wenn das DELETE in einer anderen Transaktion stattgefunden hat, dann muss es in der Aktuellen ja nicht sichtbar sein.
Verstehe ich nicht! Wenn die Datenzeile betroffen ist, die per UPDATE manipuliert werden soll, dann ist die doch sicherlich gesperrt, wenn eine andere Transaktion darauf gerade arbeitet (z.B. durch DELETE) :K Ergo könnte der Code ja nicht parallel ablaufen, sondern das UPDATE müsste auf den Schreibzugriff warten. Der kommt nach dem DELETE des anderen Prozesses irgend wann wieder und dann ist der Datensatz eben nicht mehr da, der manipuliert werden sollte...

Die Frage ist doch eher, ob einem das Vorgehen wirklich viel mehr Sicherheit verschafft, weil der Datensatz zwischen dem gecheitertem INSERT und dem UPDATE gelöscht worden sein kann?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Also zuerst ein "SELECT FOR UPDATE", dass der Datensatz gesperrt ist, falls er existiert, dann ein INSERT, damit er erzeugt wird, falls er nicht existiert und dann ein UPDATE, dass er upgedated wird.
Antworten