Michael Schröpl: regexp: Zerlegen einer Zeile mit _zwei_ unbekannten Teil-Mustern

Beitrag lesen

Hallo Forum-Leser,

ich schlage mich gerade mit einem Problem herum, für das ich keine
vernünftige Lösung zustande kriege.

Ich habe Zeilen einer Datei (Apache-Log) zu analysieren.
(Das, was ich will, kann ein Standard-Tool wie der Webalizer nicht - also
habe ich mir selbst eines geschrieben - natürlich viel spartanischer.)

Diese Zeile enthält eine Menge definierter Spalten. Die meisten davon sind
zuverlässig whitespace-frei, und der Apache verwendet ja auch Leerzeichen
als Trenner zwischen zwei Spalten.

Nun habe ich aber _zwei_ Spalten, über deren Inhalt ich nicht zuverlässig
etwas aussagen kann:

  • den URL der Anforderung (der sollte eigentlich nur URLencoded-Zeichen
      enthalten, aber weit gefehlt: Manche Browser codieren eben _nicht_ alles
      um, und der Apache schreibt gnadenlos den URL ins Log, den er empfangen
      hat, selbst wenn da Steuerzeichen drin wären oder HTML-Code) und
  • den UserAgent (da schreiben ja manche Surfer die dollsten Dinge rein -
      auch HTML-Code habe ich darin schon gefunden).

Da stehe ich nun, und möchte diese Zeile zerlegen. Aber wie erkenne ich
jetzt zuverlässig meine Felder?

Wäre es nur _ein_ solches Kamikaze-Feld drin, dann würde ein regular
expression, der ein Muster mit '^' und '$' prüft, per backtracking eine
eventuelle falsche Feld-Zerlegung erkennen und rückgängig machen.
Sinnvollerweise würde ich dieses Feld an den Schluß der Zeile legen, um
es dem Backtracker einfacher zu machen. (Das habe ich mit dem UserAgent
bereits versucht. Wenn sich jetzt noch der URL bändigen ließe ...)

Genau das scheint aber bei _zwei_ dieser Felder nicht möglich zu sein.
Denn _beide_ könnten genau mein Feld-Trennzeichen enthalten.

Ein Beispiel: (IP-Adresse, URL, UserAgent; Trennzeichen sei ein Blank)
  127.0.0.1 http://127.0.0.1/ ein URL mit Leerzeichen Ein UserAgent
Tja, wo hört jetzt der URL auf, und wo fängt der UserAgent an?

Es wird natürlich nicht besser, wenn ich die IP-Adresse in die Mitte lege:
  http://127.0.0.1/ ein URL mit Leerzeichen 127.0.0.1 Ein UserAgent
Theoretisch könnte auch "Leerzeichen" die IP-Adresse sein.

In dem letztgenannten Fall könnte ich mir scheinbar dadurch helfen,
daß ich für die IP-Adresse ein "scharfes" Muster parse, also nicht (\S+),
sondern beispielsweise (\d+.\d+.\d+.\d+). Im obigen Falle würde der
regular expression die IP-Adresse etwas zuverlässiger finden.

Aber wer hindert einen Benutzer daran, ein korrektes Muster einer IP-
Adresse in seinem UserAgent zu senden oder ein solches als Teil eines
URL zu verwenden (sagen wir mal, als QUERY_STRING):
  http://127.0.0.1/ URL mit 127.0.0.1 127.0.0.2 UserAgent
Wo steht jetzt die IP-Adresse? Wo endet der URL, wo beginnt der UserAgent?

Irgendwie fürchte ich, daß meine Aufgabenstellung, die Felder meiner
Zeile zuverlässig zu zerlegen, grundsätzlich unlösbar ist.
Ich bin mir aber der Mächtigkeit von regulären Ausdrücken nicht exakt
genug bewußt, um nicht doch einen Versuch zu starten, hier einen Tip zu
bekommen. Aber auch ein Beweis, daß ich keine Chance habe, würde mir
weiterhelfen - dann kann ich wenigstens guten Gewissens aufgeben.

Momentan behelfe ich mir damit, daß mein Auswertungsprogramm einen
geheimen, relativ komplizierten Feldtrenner verwendet.
Im Sinne der korrekten Verarbeitung ist das natürlich keine Lösung; ich
verstehe die Programmausgabe aber gut genug, um auf einen Blick erkennen
zu können, ob da etwas falsch zugeordnet ist.
Nur leider möchte ich mein Programm weitergeben ... das Problem könnte
also bei einer beliebig großen Benutzermenge in beliebiger Form auftreten.

Hätte ich eine Möglichkeit, die Apache-Protokollierung zu beeinflussen,
beispielsweise um alle Sonderzeichen explizit zu URLencoden, dann wäre
ich gerettet. Das darf aber nicht dadurch geschehen, daß ich beispiels-
weise mod_log_custom umschreibe - es sollte bei _allen_ Apaches funktio-
nieren, nicht nur bei meinem eigenen ...

In der Hoffnung auf einen Geistesblitz grüßt
       Michael