Datum und Zeit herausnehmen aus einemText mit Regex

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
kahlenberg
User
Beiträge: 3
Registriert: Mittwoch 3. Juli 2019, 08:39

Hallo,

Ich habe ein pdf Datei und kann die Datei mittels PDFMiner vom pdf zum Text konvertieren. Der Text ist folgendes:

Code: Alles auswählen

FeüwegAI +AI -Text aus ReiseMo10.06Di11.067:5017:208,458,150,30lt. E-MailMi12.066:308:58rz10,058,151,450,05Wien-Thalheim8:5817:20Do13.067:2415:5810,118,151,5615:5818:20rzThalheim-WienFr14.068:1612:414,105,30-1,20Wochensumme33,1130,152,510,05Mo17.068:1517:278,278,150,12Di18.068:2416:347,258,15-0,50Mi19.068:5617:518,108,15-0,05Do20.06F18,158,15Fr21.0611:1314:002,475,30-2,43Wochensumme26,4938,30-3,268,15Mo24.068:0316:518,038,15-0,12Di25.069:0014:224,378,15-3,38Mi26.069:3318:228,198,150,04Do27.069:1317:518,068,15-0,09Fr28.069:0112:543,395,30-1,51
Der enthält z.B. Mo10.06Di11.067:5017:208,458,150,30...
Ich möchte mit Regex folgende Zeilen generieren

Code: Alles auswählen

Mo 10.06
Di 11.06 7:50 17:20
Mi 12.06 6:30 8:58
Wie kann ich mit regex machen?
Danke.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Funktioniert fuer mich:

Code: Alles auswählen

import re

DATA = """FeüwegAI +AI -Text aus ReiseMo10.06Di11.067:5017:208,458,150,30lt. E-MailMi12.066:308:58rz10,058,151,450,05Wien-Thalheim8:5817:20Do13.067:2415:5810,118,151,5615:5818:20rzThalheim-WienFr14.068:1612:414,105,30-1,20Wochensumme33,1130,152,510,05Mo17.068:1517:278,278,150,12Di18.068:2416:347,258,15-0,50Mi19.068:5617:518,108,15-0,05Do20.06F18,158,15Fr21.0611:1314:002,475,30-2,43Wochensumme26,4938,30-3,268,15Mo24.068:0316:518,038,15-0,12Di25.069:0014:224,378,15-3,38Mi26.069:3318:228,198,150,04Do27.069:1317:518,068,15-0,09Fr28.069:0112:543,395,30-1,51"""

REX = r"(?P<day>Mo|Di|Mi|Do|Fr|Sa|So)(?P<date>\d\d\.\d\d)(?P<time>\d\d?:\d\d)"

def main():
    foo = re.findall(REX, DATA)
    print(foo)

if __name__ == '__main__':
  main()
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Für mich nicht – das sind nur 13 von 15 Treffern und es fehlt die zweite Zeit bei denen mit Zeit.

@kahlenberg: Was hast Du denn schon versucht und wo liegt das konkrete Problem? Reguläre Ausdrücke entwickelt man wie andere Teile von Programmen Stück für Stück und testet ob die Teillösung das macht was man möchte. Dafür gibt es auch Programme die dabei helfen, oder Online-Lösungen wie https://regex101.com/
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Gut gesehen, das sieht etwas besser aus:

Code: Alles auswählen

REX = r"(?P<day>Mo|Di|Mi|Do|Fr|Sa|So)(?P<date>\d\d\.\d\d)(?P<time>\d\d?:\d\d)?"
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Fehlt immer noch die zweite Zeit. 🙂
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ahhhh. Habe ich gar nicht gesehen, dass die da sein soll. Naja. Die bleibt als Aufgabe fuer den Leser :)
kahlenberg
User
Beiträge: 3
Registriert: Mittwoch 3. Juli 2019, 08:39

Danke für die Antworten, ich teste sofort.
@__blackjack__ Danke für den Link.
Benutzeravatar
snafu
User
Beiträge: 6866
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

So sollte es klappen:

Code: Alles auswählen

import re

DATA = 'FeüwegAI +AI -Text aus ReiseMo10.06Di11.067:5017:208,458,150,30lt. E-MailMi12.066:308:58rz10,058,151,450,05Wien-Thalheim8:5817:20Do13.067:2415:5810,118,151,5615:5818:20rzThalheim-WienFr14.068:1612:414,105,30-1,20Wochensumme33,1130,152,510,05Mo17.068:1517:278,278,150,12Di18.068:2416:347,258,15-0,50Mi19.068:5617:518,108,15-0,05Do20.06F18,158,15Fr21.0611:1314:002,475,30-2,43Wochensumme26,4938,30-3,268,15Mo24.068:0316:518,038,15-0,12Di25.069:0014:224,378,15-3,38Mi26.069:3318:228,198,150,04Do27.069:1317:518,068,15-0,09Fr28.069:0112:543,395,30-1,51'

def get_times(text):
    pattern = re.compile(
        r'(Mo|Di|Mi|Do|Fr|Sa|So)'  # Weekday
        r'(\d{1,2}.\d{2})'         # Date
        r'(\d{1,2}:\d{2})?'        # Start time
        r'(\d{1,2}:\d{2})?'        # End time
    )
    for match in re.findall(pattern, text):
        yield ' '.join(filter(bool, match))

def main():
    for time in get_times(DATA):
        print(time)

if __name__ == '__main__':
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Zumindest laut Beispieldaten hätte ich beide Zeiten zusammen als Optional gesehen. Also das man auch dann wenn es eine Start, aber keine Endzeit gibt, man nur Wochentag und Datum bekommt.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
snafu
User
Beiträge: 6866
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und hier habe ich den Ansatz mit benannten Gruppen aufgegriffen. Das Parsen und das Bauen der Strings habe ich nun getrennt implementiert. Anstelle von filter() wird jetzt ein Generator benutzt.

Code: Alles auswählen

import re

DATA = 'FeüwegAI +AI -Text aus ReiseMo10.06Di11.067:5017:208,458,150,30lt. E-MailMi12.066:308:58rz10,058,151,450,05Wien-Thalheim8:5817:20Do13.067:2415:5810,118,151,5615:5818:20rzThalheim-WienFr14.068:1612:414,105,30-1,20Wochensumme33,1130,152,510,05Mo17.068:1517:278,278,150,12Di18.068:2416:347,258,15-0,50Mi19.068:5617:518,108,15-0,05Do20.06F18,158,15Fr21.0611:1314:002,475,30-2,43Wochensumme26,4938,30-3,268,15Mo24.068:0316:518,038,15-0,12Di25.069:0014:224,378,15-3,38Mi26.069:3318:228,198,150,04Do27.069:1317:518,068,15-0,09Fr28.069:0112:543,395,30-1,51'

def parse_datetimes(text):
    pattern = re.compile(
        r'(?P<weekday>Mo|Di|Mi|Do|Fr|Sa|So)'
        r'(?P<date>\d{1,2}\.\d{2})'
        r'(?P<starttime>\d{1,2}:\d{2})?'
        r'(?P<endtime>\d{1,2}:\d{2})?'
    )
    for match in re.finditer(pattern, text):
        yield match.groupdict()

def make_string(mapping):
    return ' '.join(value for value in mapping.values() if value)

def main():
    for match in parse_datetimes(DATA):
        print(make_string(match))

if __name__ == '__main__':
    main()
Der Code setzt mindestens Python 3.6 voraus. Andernfalls kann es zu Abweichungen bei der Reihenfolge der Werte kommen.
Benutzeravatar
snafu
User
Beiträge: 6866
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier etwas "expliziter" und auch für ältere Python-Versionen geeignet:

Code: Alles auswählen

import re

DATA = 'FeüwegAI +AI -Text aus ReiseMo10.06Di11.067:5017:208,458,150,30lt. E-MailMi12.066:308:58rz10,058,151,450,05Wien-Thalheim8:5817:20Do13.067:2415:5810,118,151,5615:5818:20rzThalheim-WienFr14.068:1612:414,105,30-1,20Wochensumme33,1130,152,510,05Mo17.068:1517:278,278,150,12Di18.068:2416:347,258,15-0,50Mi19.068:5617:518,108,15-0,05Do20.06F18,158,15Fr21.0611:1314:002,475,30-2,43Wochensumme26,4938,30-3,268,15Mo24.068:0316:518,038,15-0,12Di25.069:0014:224,378,15-3,38Mi26.069:3318:228,198,150,04Do27.069:1317:518,068,15-0,09Fr28.069:0112:543,395,30-1,51'

def parse_datetimes(text):
    pattern = re.compile(
        r'(?P<weekday>Mo|Di|Mi|Do|Fr|Sa|So)'
        r'(?P<date>\d{1,2}\.\d{2})'
        r'('
        r'(?P<starttime>\d{1,2}:\d{2})'
        r'(?P<endtime>\d{1,2}:\d{2})'
        r')?'
    )
    for match in re.finditer(pattern, text):
        yield match.groupdict()

def make_string(mapping):
    result = ['{weekday} {date}'.format(**mapping)]
    if mapping['starttime'] and mapping['endtime']:
        result.append('{starttime} {endtime}'.format(**mapping))
    return ' '.join(result)

def main():
    for result in parse_datetimes(DATA):
        print(make_string(result))

if __name__ == '__main__':
    main()
Benutzeravatar
snafu
User
Beiträge: 6866
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Noch ein Hinweis: Am 13.06. scheint es einen Zwischenstopp bei der Strecke Thalheim-Wien zu geben. Und auch für den 12.06. sind anscheinend zwei Zeitspannen eintragen. Wie soll damit umgegangen werden?

Und mal allgemein gefragt: Woher kommen diese Daten eigentlich? Werden die von einem Fahrtenschreiber erzeugt? Gibt es eine offizielle Beschreibung zum Format der Daten?
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Laut erstem Beitrag ist das Text der aus einem PDF gekratzt wurde. Gibt es Fahrtenschreiber die PDF liefern? 🙂
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
snafu
User
Beiträge: 6866
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

__blackjack__ hat geschrieben: Mittwoch 3. Juli 2019, 18:52 @snafu: Laut erstem Beitrag ist das Text der aus einem PDF gekratzt wurde. Gibt es Fahrtenschreiber die PDF liefern? 🙂
Schon mal daran gedacht, dass die Quelle der PDF-Datei trotzdem ein Fahrtenschreiber sein könnte? :)

Und ob der von sich aus Tools mitbringt zur Umwandlung in PDF, müsste ich nachsehen. Digitale Fahrtenschreiber sind dir aber bekannt, oder?

EDIT: Es muss ja nicht tatsächlich aus einem Fahrtenschreiber kommen. So blöde Bemerkungen kann man sich trotzdem sparen...
kahlenberg
User
Beiträge: 3
Registriert: Mittwoch 3. Juli 2019, 08:39

Ich bin sehr überrascht über den vielen nützlichen Antworten.
Vielen Dank! :D
Die Daten bekomme ich als pdf.

Hintergrund:
Ich bin beschäftigt in einer Firma als Leasing-Mitarbeiter. Als Leasing-Mitarbeiter habe ich keinen Zugriff auf den Zeitdaten, jeden Monatsanfang bekomme ich meine Zeiten als eine pdf Datei in Email. Ich muss noch die Zeiten in meiner "echten" Firma eintragen. Es gibt mehrere "Aufgaben" die ich noch erledigen muss bis ich alles automatisieren kann :)
Erstens pdf Lesen. Das habe ich mit PDFMiner gemacht.
Zweitens Komm- und Gehzeiten aus der pdf herausnehmen ( 8) ).
(Ein kleines Problem: In pdf ist die Differenz von Kom-Gehzeiten als Dezimalzahl mit Komma eingetragen. Das heisst, z.B.17:30-08:00 = 09:30 Stunden. In pdf steht 9,30, aber im Portal muss ich 9,5 schreiben (Dezimal mit Komma) ).
Drittens ich möchte noch im Portal anmelden und Zeiten automatisch eintragen (steht noch in der Luft)
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Ich weiss jetzt nicht was da die blöde Bemerkung war. Dein Beitrag hat den Eindruck hinterlassen, das Du davon ausgingst, das die gezeigten Daten in dem Format aus einem Fahrtenschreiber rausfallen, als Datei oder Mitschnitt der Kommunikation vielleicht, und das man deswegen vielleicht mal eine Formatbeschreibung dafür suchen sollte. Ich habe dann halt drauf hingewiesen, dass das kein gerätespezifisches Format ist, sondern das die Daten in der Form aus einem PDF extrahiert werden wo sie als Text drin stehen. Es also keine offizielle Formatbeschreibung gibt.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@kahlenberg: hast Du schon den HTML-Export von PDFMiner ausprobiert? Vielleicht läßt sich die Information daraus ja einfacher parsen.
Antworten