Einstieg in Regex mit Java

Table of Contents

Was ist Regex?

Regex steht für Regular expressions und  ist eine Technik, Technologie und Sprache an und für sich, die verwendet werden kann zum Ausdrücken von Textmustern mit Symbolen. Und diese Symbole sind selbst Text. Damit lassen wir verschiedene Textteile und Zeichen andere Textfolgen darstellen.

Einfaches Beispiel

In Java kann Regex Beispielsweise bei Strings angewendet werden, dazu verwenden wir die Methode String.matches. Das einfachste Beispiel, ist wenn wir mit Regex in einem String nach einem String suchen:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("cat".matches("cat"));
    }
}

Der Output ist ein Boolean Wert, in diesem Fall true. Was aber wenn wir nun eines der beiden cat gross schreiben? Der Boolean wird false sein, ausser wir passen den Regex Pattern so an, dass er Gross- und Kleinbuchstaben berücksichtigt:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("Cat".matches("[Cc]at"));
    }
}

mit den eckigen Klammern [ ] kann eine OR Verknüpfung zwischen Zeichen definiert werden.

Wenn wir ein Pattern wollen, um zu prüfen ob das Wort mit einem Buchstaben von a bis f startet könnten wir entweder alle Buchstaben in der eckigen Klammer ausschreiben. Dafür gibt es glücklicherweise eine schönere Möglichkeit, wir können Ranges angeben:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("Fat".matches("[a-fA-F]at"));
    }
}

Negieren

Was wenn wir einen Pattern wollen der prüft ob etwas nicht zutrifft. Zum Beispiel ob der Erste Buchstaben des Wortes nicht ein Buchstabe von a bis f ist:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("Fat".matches("[^a-fA-F]at"));
    }
}

Das Ergebnis ist nun false. Das Zeichen am Anfang nennt sich "Caret".

Anzahl Buchstaben

Wenn wir nun ein Pattern suchen, der prüft ob unser String aus nur drei latinischen Buchstaben besteht: 

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("Fat".matches("\\w\\w\\w"));
    }
}

Der reine Regex Pattern besteht nur aus einem \ (Backslash). Damit wir diesen in Java nutzen können, müssen wir jedoch den Backslash mit einem zweiten Backslash escapen, damit Java den Backslash nicht als Java, sondern als Regex Pattern erkennen kann.

Der Regex-Pattern für Buchstaben (\w) wertet nicht nur Buchstaben von a bis z als Valid, sondern auch Zahlen und '_' (underscore).

Anzahl Zahlen

Dasselbe geht auch, wenn wir einen Pattern für die Anzahl Zahlen definieren:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("521".matches("\\d\\d\\d"));
    }
}

Buchstaben und Zahlenfolge

Was wenn wir nun eine Sammlung von Nummern auf das folgende Format prüfen müssen: 523-231-4444 (alle Zahlen sind erlaubt). Wir könnten dazu einfach aus Faulheit folgenden Pattern definieren:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523-231-4444".matches("\d\d\d-\d\d\d-\d\d\d\d"));
    }
}

Das ist ein Valider Pattern der die Problemstellung löst. Allerdings geht das schöner und einfacher mit dem Quantifier { }:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523-231-4444".matches("\d{3}-\d{3}-\d{4}"));
    }
}

Viel besser oder? Was wenn wir nun nicht nur die Bindestriche, sondern auch z.B. Punkte als Trennzeichen akzeptieren wollen? Dann können wir eine Or Verknüpfung machen anhand des oberen Beispiels:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523-231.4444".matches("\d{3}[-.]\d{3}[-.]\d{4}"));
    }
}

Was ist nun wenn wir Leerzeichen (523-231.4444) zwischen den Nummern als Valid behandeln wollen? Theoretisch können wir nun in der Or Verknüpfung (in den eckigen Klammern) einfach ein Leerzeichen einfügen. Das funktioniert, ist aber Optisch nicht ideal, da das Leerzeichen schnell übersehen wird. Expliziter geht das mit \s als Regex Zeichen für einen Leerschlag:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523 231.4444".matches("\\d{3}[-.\\s]\\d{3}[-.\\s]\\d{4}"));
    }
}

Wenn mehr als ein Leerschlag, Bindestrich oder Punkt erlaubt ist, kann hinter der "Or"-Klammer einfach ein Plus angefügt werden:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523 ..231  4444".matches("\\d{3}[-.\\s]+\\d{3}[-.\\s]+\\d{4}"));
    }
}

Was wenn keines, eines oder mehrere dieser "Or"-Verknüpften Zeichen erlaubt sein sollen? Wir verwenden '*' anstelle von '+':

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523231 .4444".matches("\\d{3}[-.\\s]*\\d{3}[-.\\s]*\\d{4}"));
    }
}

Was ist nun wenn wir genau nur eines oder keines dieser Zeichen erlauben wollen? Wir verwenden '?' anstelle von '*':

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523231-4444".matches("\\d{3}[-.\\s]?\\d{3}[-.\\s]?\\d{4}"));
    }
}

Was, wenn wir in der letzten Zahlenfolge nur 3 oder 4 Zeichen erlauben wollen? 

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523231-4444".matches("\\d{3}[-.\\s]?\\d{3}[-.\\s]?\\d{3,4}"));
    }
}

Wenn wir mindestens drei Stellen brauchen, aber es auch unbeschränkt mehr sein dürfen, können wir die zweite Zahl nach dem Komma einfach weglassen. Was wir weiter optimieren können ist, dass wir die Wiederholungen von Regex Statements zusammenfassen und nur einmal deklarieren. Dazu löschen wir eines der beiden "\d{3}[-.\s]?" Statements und setzen es im runde Klammern, gefolgt von Spitzen klammern, in der wir die Anzahl Wiederholungen angeben, in diesem Fall 2:

public class RegexPractice {
    public static void main(String[] args) {
        System.out.println("523 231 4444".matches("(\\d{3}[-.\\s]?){2}\\d{3,}"));
    }
}

Genial, oder?