Ant Regex, ignorieren von bestimmten Strings

S

Schokole

Gast
Hallo zusammen,

als Neuling in RegEx möchte ich eine Sache nachfragen und zwar geht es um die Analyse des Logs mit Ant anhand eines RegEx-Patterns.


Java:
<containsregexp expression=".*response code=500.*|.*BUILD FAILED*. | *Exception*" />

Dies funktioniert auf folgendes Log-Schnipsel:

Java:
Remote Exception OhneBerücksichtigung
RemoteException kdaskldn asdnasd jasdnas OhneBerücksichtigung  ciasidjasd 
RemoteException kdaskldn asdnasd jasdnas OhneBerücksichtigung
RemoteException OhneBerücksichtigung  ciasidjasd 
RemoteException MitBerücksichtigung
Remote Exception MitBerücksichtigung
Exception
Remote Exception MiasdasdTEst
TEstTEstTEst Failed response code=500
TEstTEstTEst Failed response code=404
TEstTEstTEst BUILD FAILED

Mein Problem ist, dass eine Remote-Exception nicht in das Pattern passen darf und zwar die "OhneBerücksichtigung".

Das kommt bei raus:
Java:
Remote Exception OhneBerücksichtigung
RemoteException kdaskldn asdnasd jasdnas OhneBerücksichtigung  ciasidjasd 
RemoteException kdaskldn asdnasd jasdnas OhneBerücksichtigung
RemoteException OhneBerücksichtigung  ciasidjasd 
RemoteException MitBerücksichtigung
Remote Exception MitBerücksichtigung
Exception
Remote Exception MiasdasdTEst
TEstTEstTEst Failed response code=500
TEstTEstTEst BUILD FAILED
Es fehlt nur:
- TEstTEstTEst Failed response code=404

Wenn das Pattern richtig ist, soll folgende Zielmenge dabei rauskommen:
Java:
RemoteException MitBerücksichtigung
Remote Exception MitBerücksichtigung
Exception
Remote Exception MiasdasdTEst
TEstTEstTEst Failed response code=500
TEstTEstTEst BUILD FAILED

Es fehlen:
- Exception in Verbindung mit OhneBerücksichtigung
- TEstTEstTEst Failed response code=404

Kann mir jemand einen Tipp geben, wie zu diesem Ergebnis komme?
Ich habe "!?" und "^" in sehr vielen Varianten versucht - bin leider kein RegEx-Experte.

Vielen Dank für jede Antwort!

Christian
 
Zuletzt bearbeitet von einem Moderator:
N

nillehammer

Gast
Das Problem dürften die vielen Sterne sein. Deine Regex mal auseinander genommen:
  • Du bietest für das Matching drei Alternativen an (
    Code:
    |
    ), d.h. es reicht, wenn eine matcht
  • Code:
    .*response code=500.*
    : Zeichen am Anfang egal, dann "response code=500", Zeichen am Ende egal. Ist im Prinzip ein String.contains("response code=500").
  • Code:
    .*BUILD FAILED*.
    : Zeichen am Anfang egal, dann "BUILD FAILE", (fehlendes D am Ende ist Absicht) dann beliebig viele Ds (wegen "D*"), dann genau ein beliebiges Zeichen am Ende (wegen ".").
  • Code:
     *Exception*
    : Beliebig viele Leerzeichen am Anfang (wegen " *"), dann "Exceptio" (fehlendes n am Ende ist Absicht!) und dann beliebig viele ns (wegen "n*")
Vielleicht hast Du damit schon die Info, wo der Fehler ist. Ansonsten beschreibe bitte mal die Struktur der möglichen Logdatensätze. "OhneBerücksichtigung" bzw. "TEstTEstTEst" steht ja für irgendwas. Jedenfalls wäre Dir wohl nicht damit geholfen, wenn hier jemand eine Regex bastelt, die auf genau diese Strings matcht, oder?
 
Zuletzt bearbeitet von einem Moderator:

Schokole

Mitglied
Hallo Nillehammer,

danke für das Aufboren - da hätte ich drauf kommen können, wobei ich sagen muss, dass ich nicht viel mit RegEx bisher machte.

Jetzt schaut es so aus:


Code:
.*response code=500.*|.*BUILD FAILED.*|.*Exception.*

Damit trifft das Pattern besser (vor allem, wenn ich an die beliebigen "n"s denke).

Und zu deiner Frage - doch es ist schon richtig so.
_Alle_ Zeilen welche eine der folgenden Strings enthält, sollen gewählt werden:
"response code=500"
"BUILD FAILED"
"Exception" [ohne "Deployment is differed"]
 
N

nillehammer

Gast
Die ersten beiden Fälle sind Brot- und Butter. Die laufen ja schon.

"Exception" [ohne "Deployment is differed"] ist etwas kniffliger. In Menschensprache heißt das: "Wenn Exception vorkommt, schaue bis ans Ende des Strings. Matche nur, wenn Deployment is differed nicht vorkommt. In Regex-Terminologie heißt dieses Verhalten "Negative Lookahead" (Lookahead=Schaue nach vorne, negative=nicht). Zur Funktionsweise nachflogender Java-Code zum testen:
Java:
  /**
   * @param args
   */
  public static void main(String[] args) throws Exception {

    // Regex mit negative lookahead "(?!..."
    String regex = ".*Exception:(?!.*Deployment is differed).*";

    Pattern pattern = Pattern.compile(regex);

    String eingabe = "Exception: Deployment is differed.";

    Matcher matcher = pattern.matcher(eingabe);

    System.out.println("expected=false, actual=" + matcher.matches());
    
    eingabe = "Exception: An unknown error has occured.";
    
    matcher = pattern.matcher(eingabe);

    System.out.println("expected=true, actual=" + matcher.matches());

  }
Du siehst, dass so ein ansich einfacher Anwendungsfall in Regex schon etwas komplexer darzustellen ist.

Tatsächlich bedeuten Lookaheads (und auch das Gegenstück Lookbehinds) eine Menge Arbeit für die Regex-Engine. Insbesondere, wenn in dem Lookahead-Teil noch ein ".*" vorkommt. Aus Performance-Sicht kann es daher angebracht sein, die Lookahead/Lookbehind Teile nicht per Regex sondern in Java-Code mit Methoden abzuprüfen, die einfach durch das hinter dem String liegende char-Array iterieren, z.B. indexOf(...) oder contains(...).

P.S. Ich habe die Strings aus Deinem Post übernommen. Könnte mir aber vorstellen, dass es "different" statt "differed" heißen soll?
 
Zuletzt bearbeitet von einem Moderator:

Schokole

Mitglied
Hallo Nillehammer,

erstmal vielen Dank, wirklich super! :)

Das Script haben wir noch nicht eingebaut - aber per Test läuft es schon mal.

Was mich sehr wundert hat, ist dass grep (Unix-Konsole) nicht die selben Ergebnisse lieferte. Ein Kollege meinte, dass die Syntax sicher unterschiedlich zu Java sei und wir irgendetwas ändern müssen - dabei müsste RegEx doch mit einer identischen Pattern immer identische Ergebnisse liefern, unabhängig der restlichen Faktoren.

Wenn ich auf folgender Website meine RegEx-Patterns teste schauts gut aus:
RegexPlanet: online regular expression testing for Java
(und dort soll die Standard Java-Implementierung genutzt werden)

Wenn ich in Zukunft wieder RegEx-Patterns bauen muss - wie sollte ich vorgehen?
Derzeit gehe ich auf irgendwelche Websiten und teste dort - scheint nicht immer zielführend zu sein...


Nochmal vielen vielen Dank und beste Grüsse aus Basel

Schoko
 
N

nillehammer

Gast
Schokole hat gesagt.:
Was mich sehr wundert hat, ist dass grep (Unix-Konsole) nicht die selben Ergebnisse lieferte. Ein Kollege meinte, dass die Syntax sicher unterschiedlich zu Java sei und wir irgendetwas ändern müssen - dabei müsste RegEx doch mit einer identischen Pattern immer identische Ergebnisse liefern, unabhängig der restlichen Faktoren.
Das grep-Kommando verwendet eine vereinfachte Matching-Syntax (ist meiner Meinung nach nicht mal ein richtiger RegEx-Dialekt). Advanced Features wie Lookahead-/behind sind da nicht eingeschlossen. Auf den meisten Unixen unterstützt grep aber auch die erweiterte Syntax. Das muss man beim Aufruf von grep über Kommandozeilenparameter(
Code:
-E
oder
Code:
--extended-regexp
) angeben. Vielfach ist auch das Kommando egrep vorhanden, das dasselbe tut. Aber auch mit erweiterten RegExen kann es sein, dass es kleine Unterschide in der Syntax gibt. Ein Vergleich der man-Page von grep und des Javadoc der Klasse Pattern kann daher nicht schaden.

Schokole hat gesagt.:
Wenn ich in Zukunft wieder RegEx-Patterns bauen muss - wie sollte ich vorgehen?
Sofern noch nicht geschehen, acker Dich durch Dokus/Tutorials. Die Seite Regular-Expressions.info - Regex Tutorial, Examples and Reference - Regexp Patterns kann ich sehr empfehlen. Damit habe ich gelernt und sie ist auch immer wieder Anlaufpunkt, wenn ich was nachschlagen will.

Dann bastel dir eine kleine Klasse nur mit main-Methode, in der du sowas machst, wie ich in meinem letzten Post. Bau Dir die regex, bau Dir das Pattern, rufe es mit einigen wohldefinierten Testdaten auf.

Beim bauen der Regex gehe schrittweise vor. Erstmal ein konstanter Strings mit
Code:
.*
davor und dahinter. Zunächst prüfen, ob das Matching wie erwartet arbeitet. Dann den nächsten konstanten Sring mit
Code:
.*
dahinter, wieder testen usw.

Bei den variablen Teilen jeweils einzeln gucken, wie man sinnvoll Character-Classes (
Code:
[]
) bilden kann, um aus den
Code:
.*
etwas Spezifischeres zu machen. Jeweils nach jeder kleinen Änderung testen, ob es immer noch richtig gematcht wird.

Dann schauen, ob man mit Klammerung sich wiederholende Teile einkreisen kann, dadurch die Regex ggf. verkürzen. Beispiel IP-Adresse:
Code:
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
Wie man sieht, wiederholt sich das Konstrukt
Code:
\d{1,3}\.
(ein- bis dreistellige Ziffer gefolgt von einem Punkt). Man kann die Regex also verkürzen zu
Code:
(?:\\d{1,3}\\.){3}\\d{1,3}
.

Und zu guter Letzt, den Regexen nicht zu viel abverlangen. Ggf. durch andere sinnvolle Mechanismen ergänzen. Z.B. die obige IP-Regex. Die würde ungültige IPs matchen (drei Ziffern könnte z.B. auch 999 sein, 999 ist kein gültier Wert). Aber die Regex, die das abfängt, ist ziemlich hässlich. Durch die obige Regex ist sicher gestellt, dass die IP die erwarteten Punkte und sonst Ziffern enthält. Für eine fehlerfreie Bearbeitung mit String.split(".") und danach Integer.parseInt reicht das und mit den int-Werten kann man viel besser prüfen, ob der Wert zwischen 0 und 255 liegt.
 
Zuletzt bearbeitet von einem Moderator:

Schokole

Mitglied
Vielen Dank für deine Hilfe, wirklich ganz toll! :)

Ich werde mir sowas mal machen - dauert ja nicht die Welt, noch habe ich es nicht.

Mittlerweile kam auch schon die nächste Anforderung - wir sollen jetzt in den LogScripts die Ausgabe für einen "string" unterdrücken. Ich dachte mir das geht doch wieder mit einem Lookupahead.

Da es nicht klappt, will ich einfach mal mein Vorgehen beschreiben.

Zuerst brauche ich
Code:
.*

Dann brauche ich die Einschränkung
Code:
.*(?!.*string.*)

Lief so nicht. Google half mir - ich brauch "boundery" und alles als "word":
Code:
.*(?!.*string.*\b)\b\w+
(100%ig kam mir nicht wieso - wahrscheinlich weil ich die Tutorials auch recht schnell gelesen habe...)

Jetzt schaut es so aus:
Code:
"strinxgf" ==> Treffer --> Richtig
"String" ==> Treffer --> Falsch (Case Insensitive)
"ajdnasjkddn StrIng fasdasd"  ==> Treffer --> Falsch (Case Insensitive & funktioniert nur am Ende)
"asdnasdn string asldmas" ==> Treffer --> Falsch (funktioniert nur am Ende)
"string" ==> kein Treffer --> Richtig
"STRING" ==> ==> Treffer --> Falsch (Case Insensitive)
"asdasd STRING asd" ==> ==> Treffer --> Falsch (Case Insensitive)
"asdasd string" ==>  kein Treffer --> Richtig
"adasdas" ==> Treffer --> Richtig

Problem - er findet den "string" nur klein geschrieben und wenn er am ende einer Zeile steht.
Das ginge so nicht - ich werde noch mal schauen. Bin dennoch weiterhin für jeden Tipp dankbar!


Viele Grüsse

Schoko
 
N

nillehammer

Gast
Hier brauchst du kein Lookahead. Die Anforderung ist doch "nur": Unterdrücke, wenn "string" irgendwo vorkommt. Es kommt hier nur auf das Vorhandensein an, in keiner Weise darauf, ob etwas bestimmtes folgt. Das entspricht einem einfachen contains und da hatten wir weiter vorne schon ein paar Beispiele zu.

Lookahead brauchst Du nur, wenn du ein Pattern hast (z.B. "Exception"), bei dem du sicherstellen willst, dass etwas bestimmtes folgt (z.B. "Deployment is differed") oder eben nicht folgt.

Boundaries brauchst Du nur, wenn Dir in irgendeiner Weise die Positionen wichtig sind. Das ist hier nicht der Fall.

Regexen sind erstmal immer Case-Sensitive. In Java kann man über das Setzen von Flags bei Pattern.compile auch Case insensitive Patterns erzeugen. Die Flags sind als Konstanten in der Klasse Pattern definiert. Schau sie Dir also dort an. Mann kann aber auch im RegeEx-String selbst Einflus auf Casesensitivity nehmen. Die Syntax dafür darfst du Dir selbst raussuchen :D
 
Zuletzt bearbeitet von einem Moderator:

Schokole

Mitglied
Okay, verstehe schon.

Nur das Contains schaut ja so aus:
.*string.*

so zu verstehen:
. => belibeiges Zeichen
* => beliebig viele (vorgenannte) Zeichen
string
. => belibeiges Zeichen
* => beliebig viele (vorgenannte) Zeichen

So und wie negiere ich das...
mit einem [^...] lasse ich die Zeichen nicht mehr zu (srting, wäre dementsprechend ein Treffer, sollte keiner sein)

(!?string) war meine Idee - klappt ja wie gesagt nicht so wie gedacht.
 
N

nillehammer

Gast
Ahh, Mist, nicht genau genug gelesen! Hast natürlich Recht mit der Negation. Das ist mit RegEx tatsächlich nur mit Lookahead/Lookbehind zu machen. Kannst Du denn nicht "extern" negieren? Sprich in der Anwendung ein "!" oder -falls es sowas gibt- in der Konfigdatei? Es lohnt sich in die Richtung zu forschen. Das andere ist nämlich richtig hässlich. Kannst ja mal bei google nach "regex not contains string" suchen. Das, was da beschrieben ist, halte ich nicht mehr für wartbar. Weil solche Regex-Konstructe nicht mal du selbst nach einer Woche noch verstehst.
 

Schokole

Mitglied
Hallo Nillehammer!

Ich werde das mal mit dem negieren in ANT anschauen - glaube jedoch, dass dort wenig geht. Zum raus fischen von einzelnen Zeilen wird ja Regex genutzt...

Was ich bisher herausfinden konnte ist, dass ".*(?!.*string.*\b)\b\w+" nicht tut.
Probleme sind zum einen, dass der "string" am Ende stehen muss - keine Ahnung, warum er nicht in der Mitte stehen darf.
Die Regel soll ja heissen - wenn "string" in Bereich steht --> NICHT berücksichtigen, leider wird es berücksichtigt, wenn es in der Mitte steht.

Weiterhin bekomme ich Case-Insensitive nicht hin --> sowohl mit \i ... \ als auch mit (?i) ... (?-i) funktionieren nicht.

Noch eine Idee? :)


Beste Grüsse

Schoko
 

Oben