Regex mit Umlauten

werdas34

Bekanntes Mitglied
Hallo,

ich brauche etwas Unterstützung bei einem RegEx.
Ziel ist einen Text auf einen Substring zu überprüfen, und wenn dieser Substring vorkommt, soll das Wort als ganzes zurückgegeben werden.
Text kann alles sein, Doku, Quellcode, Konfigurationsdatein. Daraus kann kein Muster erschlossen werden. Das extrahierte Wort soll ohne Sonderzeichen und aus dem deutschem Alphabet (mit Umlaut und ß) bestehen.

Der bisherige RegEx scheint nicht so ganz zu funktionieren.
Auskommentiert ist die erwartete Lösung und unten der tatsächliche Output.
Wie man erkennt machen oft die Umlaute Probleme, aber auch nicht immer. "seiner" und "useiwsaö" werden gar nicht gefunden.

Java:
public class Renamer {
    public static void main(String[] args) {
        String text = "Das soll so sein.\"seinetwegü&en so <äzuse-123 <AeDwertse! ?seiner_ .seiöner_ ID_useiwsaö_ps -gfserTßz-";
        // sein
        // seinetwegü&en
        // äzuse
        // AeDwertse
        // seiner
        // seiöner
        // useiwsaö
        // gfserTßz
        String substring = "se";
        List<String> matchingWords = findWordsContainingSubstring(text, substring);

        for (String word : matchingWords) {
            System.out.println(word);
        }
    }

    public static List<String> findWordsContainingSubstring(String text, String substring) {
        String regex = "\\b[a-zA-ZäöüÄÖÜß]*" + Pattern.quote(substring) + "[a-zA-ZäöüÄÖÜß]*\\b";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);

        List<String> matchingWords = new ArrayList<>();
        while (matcher.find()) {
            matchingWords.add(matcher.group());
        }
        return matchingWords;
    }
}

Code:
sein
seinetweg
zuse
AeDwertse
seiö
gfserTßz
 
Evtl.:
Java:
return Arrays
    .stream(text.split("[\\s\\./\"\\-_<\\?]+"))
    .filter(s -> s.contains(substring))
    .toList();
 
Ist mir gerade noch eingefallen... Es geht vermutlich noch besser:
Java:
        return Arrays
            .stream(text.split("[\\s\\p{Punct}&&[^&]]+"))
            .filter(s -> s.contains(substring))
            .toList();
 
Danke für deine Ideen.
Du versuchst es quasi andersherum. Alle nicht gewünschten Zeichen aufräumen.
Den Ansatz habe ich auch schon verfolgt, aber wieder verworfen, da der Textinput wirklich alles sein kann. Und weil ich jedes Sonderzeichen extra angeben müsste (schlechte Lesbarkeit).

Hätte jetzt aufbauend auf deinem, das hier probiert.
Java:
        return Arrays
                .stream(text.split("[^a-zA-ZäüöÄÜÖß]+"))
                .filter(s -> s.contains(substring))
                .toList();
So muss ich nicht alle Sonderzeichen angeben, die gefiltert werden sollen. Klappt auch besser als davor.

Siehst du irgendwo Probleme die ich bekommen könnte?
 
Dein Problem ist u.a. /b
Word bundary - endet quasi bei verschiedenen Zeichen. Somit bekommst du kein Wort, welches bspw. ein & enthält.
 
Ja das word boundary hat mir anfänglich Probleme bereitet, da unter anderem 0-9 und _ als Wortzeichen zählen.

Bei einem & würden mich nur das Wort davor und das Wort danach interessieren. Eins&Zwei -> [Eins, Zwei]
 
Vermutlich muss die Anforderung besser beschrieben werden. Es wird laut #1 folgende Ausgabe erwartet:
Java:
        // sein
        // seinetwegü&en
        // äzuse
        // AeDwertse
        // seiner
        // seiöner
        // useiwsaö
        // gfserTßz
Jetzt schreibst du aber:
Bei einem & würden mich nur das Wort davor und das Wort danach interessieren.
Was ist jetzt richtig: seinetwegü&en oder [Eins, Zwei]?

Die Folge: [a-zA-Z] ist erst mal überflüssig und unperformant. Man kann Regex einfach auf 'ignore case' stellen, damit muss hier nur die Hälfte der Fälle geprüft werden: [a-z].

Du möchtest aber selbst Satzzeichen mit in die Trenner einbeziehen. Also kannst du jetzt \s und eine Zeichengruppe, welche Satzzeichen enthält verwenden. das wäre dann: [\\s\\p{Punct}]+ - aber dein Beispiel: // seinetwegü&en also [\\s\\p{Punct}&&[^&]]+.

Offensichtlich bist du damit aber nicht zufrieden. Jetzt wäre also der Moment, deine Beschreibung zu überdenken. Wenn jemand in seiner Beschreibung das Wort
verwendet, dann ist eigentlich schon klar, dass die Beschreibung falsch ist. Es ist niemals wirklich 'alles' gemeint - hier bspw. Whitespaces würden imho auch zu 'alles' gehören - also müsste man garnichts trennen.
 
Ja das seinetwegü&en sollte statt dem & ein ß enthalten. Ist mir durch die ganzen Buchstaben nicht mehr aufgefallen. Sorry.

Ich hatte im Eingangspost auch formuliert:
Das extrahierte Wort soll ohne Sonderzeichen und aus dem deutschem Alphabet (mit Umlaut und ß) bestehen.
Aber verstehe die Irritation.

Das mit den ignoreCase beim Regex ist interessant. Habe ich mir notiert.
Ist das mit dem ignoreCase wirklich effizienter? Schließlich muss dennoch auf Klein- bzw Großbuchstaben abgeglichen werden?
 
Zuletzt bearbeitet:
Ich verstehe das Problem nicht ganz. Du definierst ein Wort als Folge von Zeichen, die dem deutschen Alphabet entsprechen. Damit hat es sich doch aber auch schon, oder sehe ich hier was falsch?

Im Übrigen ist "[A-Za-zÄÖÜẞäöüß]" nicht identisch mit "(?i)[a-zäöüß]":

ZeichentoLowerCasetoUpperCaseRegex
ß (kleines Eszett)ß (kleines Eszett)SS(?i)[ß] matched nur ß, nicht aber SS oder ẞ (großes Eszett)
ẞ (großes Eszett)ß (kleines Eszett)ẞ (großes Eszett)(?i)[ẞ] matched nur ẞ, nicht aber ß (kleines Eszett)
 

Zurück
Oben