Regex-Gruppen

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.
Taadu
User
Beiträge: 7
Registriert: Donnerstag 11. Mai 2017, 10:40

Ich bin mir nicht sicher, ob ich hier richtig bin, aber ich versuche es einfach mal. Ich habe mal eine Frage zu Regex, ich muss einen Apachelog nach bestimmten Vorgaben ana­ly­sie­ren lassen, dabei kann ich eine solche Beispielzeile aus der Datei: 172.16.11.12 - - [26/May/2013:14:06:05 +0200] \"GET /navleiste/d_szut_navleiste.gif HTTP/1.1\" 200 1776 mit Hilfe von (r"^(.*?) (.*?) (.*?) \[(.*?)\] \"(.*?) (.*?) (.*?)\" (.*?) (.*?)$") zumindest schonmal abdecken, jedoch weiß ich nicht genau, wie ich die einzelnen "Gruppen" in anderer Reihenfolge bzw. alleine abrufen kann. Möglicherweise mache etwas Grundlegend falsch oder übersehe etwas.
MfG.
Taadu
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Möglicherweise suchst Du "named groups".
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

die Gruppen sind durchnummeriert und mit der `group()` Methode kannst du drauf zugreifen. Beispiel (Python 3.5):

Code: Alles auswählen

>>> my_string = '172.16.11.12 - - [26/May/2013:14:06:05 +0200] \"GET /navleiste/d_szut_navleiste.gif HTTP/1.1\" 200 1776'
>>> import re
>>> regex = r'^(.*?) (.*?) (.*?) \[(.*?)\] \"(.*?) (.*?) (.*?)\" (.*?) (.*?)$'
>>> p = re.compile(regex)
>>> m = p.match(my_string)
>>> m.group()
'172.16.11.12 - - [26/May/2013:14:06:05 +0200] "GET /navleiste/d_szut_navleiste.gif HTTP/1.1" 200 1776'
>>> m.group(0)
'172.16.11.12 - - [26/May/2013:14:06:05 +0200] "GET /navleiste/d_szut_navleiste.gif HTTP/1.1" 200 1776'
>>> m.group(1)
'172.16.11.12'
>>> m.group(5)
'GET'
>>> m.group(6)
'/navleiste/d_szut_navleiste.gif'
>>>
Mit `groups()` siehst du alle Capture-Gruppen:

[codebox=pycon file=Unbenannt.txt]>>> m.groups()
('172.16.11.12', '-', '-', '26/May/2013:14:06:05 +0200', 'GET', '/navleiste/d_szut_navleiste.gif', 'HTTP/1.1', '200', '1776')
>>>
[/code]

Gruß, noisefloor
Taadu
User
Beiträge: 7
Registriert: Donnerstag 11. Mai 2017, 10:40

Vielen Dank für die Antworten, sie haben mir sehr weitergeholfen, ich werde das sobald wie möglich ausprobieren.
Ich war gerade dabei einen Beitrag zu verfassen, in dem ich um ein Beispiel bitte und noch davor wurde eines bereitgestellt.
beertonic
User
Beiträge: 37
Registriert: Montag 8. Mai 2017, 15:26

Hier ein Bsp. für named groups:

Code: Alles auswählen

r = '^(?P<ip>[\d\.]*)[^\[]*(?P<datum>[^\]]*)[^\"]*\"(?P<befehl>[^ ]*) (?P<pfad>[^ ]*) (?P<protokol>[^\"]*)[^\d]*(?P<zahlen>.*)$'
Gruppen mit Namen sind einfach übersichtlicher.
Taadu
User
Beiträge: 7
Registriert: Donnerstag 11. Mai 2017, 10:40

Ich bin jetzt nach dem Beispiel von noisefloor vorgegangen und bekomme nun auch eine Ausgabe wie ich sie brauche bzw. haben möchte, jedoch habe ich jetzt das Problem, das ich am Ende eines Durchlaufs folgenden Fehler bekomme:
print((m.group()))
AttributeError: 'NoneType' object has no attribute 'group'

Ich verstehe jetzt nicht genau, wieso ich zwar die richtige Ausgabe bekomme wenn ich die jeweilige Gruppe ausgebe, aber ich trotzdem diese Meldung bekomme.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

den Fehler kann ich so nicht nachvollziehen. Ich kann `m.group()` nicht nachvollziehen bzw. den Befehl beliebig oft aufrufen.

Was meinst mit
das ich am Ende eines Durchlaufs folgenden Fehler bekomme
? Bzw. welche Durchlauf meinst du? Benutzt du irgendwo einen Iterator?

Gruß, noisefloor
Taadu
User
Beiträge: 7
Registriert: Donnerstag 11. Mai 2017, 10:40

noisefloor hat geschrieben: Bzw. welche Durchlauf meinst du? Benutzt du irgendwo einen Iterator?
Gruß, noisefloor

So sehr kenne ich mich mit der Materie nicht aus, ich kann zu Veranschaulichung aber eine runtergebrochene Version meines Quelltextes Zeigen.

Code: Alles auswählen

import re

antwort = open("antwort.txt", "a")



Pfad = "c:/Users/talib/Desktop/apache.log"
try:
    daten = open(Pfad)
except:
    print("Dateizugriff nichterfolgreich")
    exit(0)

for zeile in daten:
    regex = (r"^(.*?) (.*?) (.*?) \[(.*?)\] \"(.*?) (.*?) (.*?)\" (.*?) (.*?)$")
    p = re.compile(regex)
    m = p.match(zeile)
    print(m.group())

antwort.close()
In der Ausführung listet er mir, so wie ich das sehe alle Zeilen auf, aber zudem auch diese Meldung:
Traceback (most recent call last):
File "C:/Users/talib/PycharmProjects/untitled1/16 test.py", line 18, in <module>
print(m.group())
AttributeError: 'NoneType' object has no attribute 'group'
Zuletzt geändert von Anonymous am Montag 15. Mai 2017, 11:36, insgesamt 3-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Taadu: eine der Zeilen in der Datei passt vermutlich nicht auf die regex, daher wird kein Match-Objekt sondern None zurückgegeben.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

teste doch mal vor dem `print`, ob `m ` überhaupt existiert:

Code: Alles auswählen

...
if m:
    print(m.group())
Zum Code: nackte `try ... except` sind schlecht, weil du _alle_ Fehler abfängst. Wenn du z.B. einen SyntaxError im Code hättest, dann würde bei dir auch "Datenzugriff nicht erfolgreich" stehen was mindestens mal irreführend ist. Wenn fängt man gezielt bestimmte Fehler ab.

Wenn bei dir eine Exception geworfen würde, würde die Datei nicht geschlossen, weil die Zeile mit `antworten.close()` nie erreicht würde. Das müsste wenn noch in einen `finally` Block.

Zum Öffnen von Dateien nimmt man normalerweise das `with` Statement, weil sich Python dann darum kümmert, dass die Datei auch wieder geschlossen wird.

Gruß, noisefloor
BlackJack

@Taadu: Noch eine Anmerkung zum Quelltext: Das `re.compile()` macht so keinen Sinn, denn wenn Du das gleiche Muster in jedem Schleifendurchlauf immer wieder in ein RegEx-Objekt übersetzt, könntest Du auch gleich `re.match()` verwenden.

Einbuchstabige Namen sind keine gute Idee. Ich rätsele immer noch was `p` eigentlich bedeuten soll.

`exit()` sollte man nur verwenden wenn es wirklich benötigt wird, was bei einer 0 als Argument eigentlich nie der Fall ist, denn das passiert auch ganz automatisch wenn das Programm am Ende angelangt ist. Und man kann Programme eigentlich immer so strukturieren, dass das Programmende mit dem ”natürlichen Ende” zusammenfällt. Ausnahme ist der Fall wo man explizit andere Werte als 0 als Rückgabecode an den aufrufenden Prozess übermitteln möchte. Ausserdem müsste man `exit()` aus dem `sys`-Modul importieren. Das es auch ohne geht ist ein undokumentiertes Implementierungsdetail von CPython.

Warum behandelst Du die Ausnahme *überhaupt*? Denn das Programm würde ja auch ohne die Behandlung bei einer Ausnahme abbrechen. Sogar mit mehr Informationen über das Problem was aufgetreten ist.
Taadu
User
Beiträge: 7
Registriert: Donnerstag 11. Mai 2017, 10:40

Vielen Dank für die Rückmeldung alle Antworten waren mir wirklich sehr Hilfreich, auch der Hinweis das mein Regex nicht alles Abdecken könnte, hat sich bewahrheitet, das kam für mich eigentlich garnicht in Frage...
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Einbuchstabige Namen sind keine gute Idee. Ich rätsele immer noch was `p` eigentlich bedeuten soll.
Das ist natürlich korrekt - im gegebenen Fall sind die Variablennamen an das offizielle Python-Howto zur Regex angelehnt: https://docs.python.org/3.5/howto/regex ... egex-howto

Kannst du dir jetzt aussuchen, ob das meine Post besser macht oder das Howto schlechter ;-)

Gruß, noisefloor
BlackJack

@noisefloor: Bei Deinem Code kann man ja noch sagen das ist kein Quelltext sondern eine Livesitzung in einer Python-Shell. Das ist ja alles kurz und übersichtlich und da hat so etwas wie leicht zu tippen gegenüber nach Jahren und mehreren Überarbeitungen noch verständlich zu lesen, auch einen anderen Stellenwert. :-)
beertonic
User
Beiträge: 37
Registriert: Montag 8. Mai 2017, 15:26

Ich würde dir empfehlern (.*?) zu vermeiden. Es ist zu unpräzise, weil es fast alles matcht.
Ich benutze sowas wie ([^#]*). Es matcht solange das Zeichen nicht # ist, aber da kann man natürlich alles einsetzen.
BlackJack

Ich würde beertonic da zustimmen. Insbesondere da wir ja jetzt auch wissen das dort durchaus Zeilen vorkommen die nicht dem Muster entsprechen, würde ich sicherstellen wollen das es nicht so leicht passieren kann, dass man Zeilen matcht die man gar nicht haben möchte weil das Suchmuster so/zu unscharf formuliert ist. Ausserdem würde ich „named groups“ mindestens für die Teilausdrücke verwenden, die für die Auswertung interessant sind. Das macht den Code verständlicher als irgendwelche magischen Indexzahlen.
beertonic
User
Beiträge: 37
Registriert: Montag 8. Mai 2017, 15:26

Ich habe weiter oben auch schon eine präzisere Version mit named groups gepostet.
Taadu
User
Beiträge: 7
Registriert: Donnerstag 11. Mai 2017, 10:40

Danke für die Hilfreichen Tipps, jedoch habe eine Frage, die nicht mehr viel mit Regex zu tun hat, ich frage mich gerade, ob es möglich ist eine Variable wie eine Textdatei zu beschreiben also vom Aufbau her. Ich habe nämlich das problem, das ich eine bestimmte Zeilenstruktur nur am Ende bzw. Anfang ausgeben will, also muss ich sie vorher irgendiwe zwischenspeichern (möglicherweise in einer Liste?), erst wollte ich mit Hilfe eines Zählers automatisch immer neue Variablen erstellen lassen, um diese dann am Ende in die Textdatei schreiben zu lassen, dann habe ich hier im Forum aber gelesen das dies vermieden werden sollte, vielleicht hat auch hier jemand einen Vorschlag für mich.

Mit einem solchen versuch

Code: Alles auswählen

        ausgabe = (zeile +zeile)
hat er "ausgabe" nur immer mit der neusten "Wert" überschrieben
beertonic
User
Beiträge: 37
Registriert: Montag 8. Mai 2017, 15:26

Nimm eine Liste

Code: Alles auswählen

liste.append(zeile+zeile)
Wenn du Variablen bis zum nächsten Programmaufruf speichern willst kannst du das mit pickle machen, oder einfach in eine Datei schreiben.
http://www.python-kurs.eu/dateien.php
BlackJack

@Taadu: Sammel die Daten doch einfach in einer Liste. Oder schreibe sie vor bzw. nach den anderen Zeilen in die Textdatei.
Antworten