Laden...

Regex bauen für Abgleich von String mit Platzhaltern

Erstellt von timbu42 vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.954 Views
T
timbu42 Themenstarter:in
31 Beiträge seit 2017
vor 5 Jahren
Regex bauen für Abgleich von String mit Platzhaltern

Hallo Zusammen,

ich möchte eine einfache Klasse PlatzhalterString bauen, der ich einen string (=Muster) übergebe, welcher Platzhalter enthält, z.B. in Form von "[PH]". Neben dem Konstruktor soll die einzige Methode dieser Klasse IsMatch(string) sein, die prüft, ob der angegebene string zum Muster passt, wie folgt:



PlatzhalterString ps = new PlatzhalterString("Ich mag [PH] und [PH] sehr", "[PH]");

ps.IsMatch("Ich mag Äpfel und Bananen sehr");                    // -> true
ps.IsMatch("Ich mag Kiwis sehr");                                // -> false
ps.IsMatch("Ich hasse Äpfel und Bananen sehr");                  // -> false
ps.IsMatch("Ich mag Äpfel und Bananen sehr, Hugh!");             // -> false


Anstatt des Platzhalters darf im zu testenden string also irgendetwas stehen, aber das "Grundgerüst" (alles außer Platzhalter) muss erhalten bleiben.

Kann jemand mir den besten Weg nennen, um die Klasse zu implementieren?

Ich habe dafür mit der Regex-Klasse herumgespielt, habe aber noch keinen guten Weg gefunden.

Vielen Dank im Voraus,
Tim

2.079 Beiträge seit 2012
vor 5 Jahren

Dafür brauchst Du keine eigene Klasse, erstelle einfach eine Instanz der Regex-Klasse und nutze dort die IsMatch-Methode.
Und das geht definitiv 😉

Wie das geht, kannst Du hier lernen: Regex-Tutorial
Hilfreich ist auch diese Seite: https://regex101.com/

PS:
Wenn der Platzhalter unbedingt "[PH]" sein muss, dann macht halt vorher ein Replace auf den String und ersetze das "[PH]" mit dem relevanten Regex-Teil.

49.485 Beiträge seit 2005
vor 5 Jahren

Hallo timbu42,

was genau hast du schon versucht und warum siehst du den anscheinend schon gefundenen Weg als schlecht an?

Die simpelste Formulierung für "irgendwas" in Regex lautet .*

Wie von Palladin007 vorgeschlagen kannst du den Platzhalter im Suchstring einfach dadurch ersetzen.

Vorher müsstest du allerdings noch Regex.Escape auf den Suchstring anwenden, damit alle darin enthaltenen Zeichen auch wirklich auf sich selbst und nur auf sich selbst passen.

herbivore

PS: Je nach Anforderung könnte es aber auch sein, dass "irgendwas" doch spezieller (z.B nur auf ganze Worte) oder allgemeiner (z.B. auch auf Zeilenvorschübe) passen soll. Dann müsste man den genannten Grundpattern noch abändern. Dabei hilft das genannte Regex-Tutorial und beim Experimentieren auch der On-the-fly Regex-Tester: Regex-Lab.

T
timbu42 Themenstarter:in
31 Beiträge seit 2017
vor 5 Jahren

Danke für die Antwort! So in der Art habe ich es auch versucht.

Leider ist es m.E. nicht ganz so einfach.

Ich kann den Platzhalter problemlos mit Replace() durch ".*" ersetzen, das sind also beliebig viele beliebige Zeichen.

Aber wenn ich folgendes aufrufe:

Regex.IsMatch("Ich mag Äpfel und Bananen sehr, Hugh!", "Ich mag .* und .* sehr");

kommt ja true raus, weil die Regular Expression im Input-string (erster Parameter) passend auftaucht (unter Berücksichtigung des Platzhalters ".*"). Es soll aber false zurückgeben, weil der hintere Teil nicht passt.

Weitere Probleme ergeben sich, wenn das Muster Zeichen wie ".", "+" oder "*" enthält. Die sollen dann auch genau so gesucht werden, d.h. das Muster an sich soll nicht als Regex interpretiert werden.

49.485 Beiträge seit 2005
vor 5 Jahren

Hallo timbu42,

für das erste Problem reicht es ^ und $ am Anfang und Ende des Suchstrings hinzuzufügen.

Für das zweite Problem gibt es das schon genannte Regex.Escape.

herbivore

PS: Natürlich alles in der richtigen Reihenfolge!

T
timbu42 Themenstarter:in
31 Beiträge seit 2017
vor 5 Jahren

Hallo Herbivore,

Danke für die Antworten! Das sind so Sachen, die einem fehlen, wenn man zum ersten Mal mit Regex arbeitet.

Jetzt funktioniert es, auch wenn ich noch ein wenig mit Split() basteln musste. Vielleicht geht das auch eleganter, aber ich muss ja einerseits die Platzhalter im Input mit Replace() ersetzen und andererseits den Input durch Escape() jagen und beide Reihenfolgen (Replace(Escape()) und Escape(Replace()) führen zu Fehlern, wenn der Platzhalter ein "Escape-Zeichen" enthält.

Danke und viele Grüße,
Tim

Falls es jemanden interessiert:


    /// <summary>
    /// Diese Klasse besteht aus einem string, der Platzhalter enthält, z.B. "Ich mag [x] und [x].", mit Platzhalter = "[x]";
    /// Sie kann mit IsMatch() testen, ob eine andere Zeichenkette zu ihr passt, z.B.
    ///     a) "Ich mag Äpfel und Bananen."       passt -> true
    ///     b) "Ich mag Kiwis."                   passt nicht -> false
    ///     c) "Ich hasse Äpfel und Bananen."     passt nicht -> false
    ///     d) "Ich mag Äpfel und Bananen. Hugh." passt nicht -> false
    /// </summary>
    public class PlatzhalterString
    {

        private Regex regex;
        

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="text">Der Text</param>
        /// <param name="platzhalter">Der Platzhalter</param>
        public PlatzhalterString(string text, string platzhalter)
        {
            string[] platzhalters = new string[1] { platzhalter };
            string[] teile = text.Split(platzhalters, StringSplitOptions.None);

            StringBuilder sb = new StringBuilder();
            sb.Append("^");  // "\\A" oder "^"
            sb.Append(Regex.Escape(teile[0]));
            for (int j = 1; j < teile.Length; j++)
            {
                sb.Append(".*");
                string teil = Regex.Escape(teile[j]);
                sb.Append(teil);
            }
            sb.Append("$");  // "\\z" oder "$"

            string textRegex = sb.ToString();
            regex = new Regex(textRegex);
        }  // Konstruktor


        /// <summary>
        /// Gibt an, ob der Input-String zum Platzhalter-String passt.
        /// </summary>
        public bool IsMatch(string input)
        {
            return regex.IsMatch(input);
        }  // IsMatch()


Mit bisher folgenden NUnit-Tests:


        [Test]
        public void TestSimple1()
        {
            PlatzhalterString ps = new PlatzhalterString("Ich mag [x] und [x] sehr", "[x]");

            Assert.True(ps.IsMatch("Ich mag Äpfel und Bananen sehr"));
            Assert.False(ps.IsMatch("Ich mag Kiwis sehr"));
            Assert.False(ps.IsMatch("Ich hasse Äpfel und Bananen sehr"));
            Assert.False(ps.IsMatch("Ich mag Äpfel und Bananen sehr, Hugh!"));            
        }


        [Test]
        public void TestSimple2()
        {
            PlatzhalterString ps = new PlatzhalterString("ref[[[Alle]]]", "[[[Alle]]]");

            Assert.True(ps.IsMatch("ref"));
            Assert.True(ps.IsMatch("ref:"));
            Assert.True(ps.IsMatch("ref:coordinates"));
            Assert.True(ps.IsMatch("ref:ASDFSGSD."));
            Assert.False(ps.IsMatch("aref"));
            Assert.False(ps.IsMatch("bref:"));
            Assert.False(ps.IsMatch("cref:hallo"));
        }


        [Test]
        public void TestSimple3()
        {
            PlatzhalterString ps = new PlatzhalterString("Hallo[PH]Hier[PH]IstDerTim", "[PH]");

            Assert.True(ps.IsMatch("HalloHierIstDerTim"));
            Assert.True(ps.IsMatch("Hallo42Hier42IstDerTim"));
            Assert.True(ps.IsMatch("HalloDuHierObenIstDerTim"));
            Assert.False(ps.IsMatch("HalloHierIstDerTimX"));
            Assert.False(ps.IsMatch("XHalloHierIstDerTim"));
            Assert.False(ps.IsMatch("HalloHierIsttDerTim"));
        }


        [Test]
        public void TestSonderzeichen()
        {
            PlatzhalterString ps = new PlatzhalterString("A*[PH]A+", "[PH]");

            Assert.True(ps.IsMatch("A*TimA+"));
            Assert.True(ps.IsMatch("A*A+"));
            Assert.False(ps.IsMatch("AAAAAA"));     // Prüfen, dass * und + nicht als Teil der Regex interpretiert werden
            Assert.False(ps.IsMatch("AATimAA"));
        }

49.485 Beiträge seit 2005
vor 5 Jahren

Hallo timbu42,

mit textRegex = Regex.Escape (text).Replace (Regex.Escape (platzhalter), ".*") sollte es eigentlich in jedem Fall gehen.

herbivore

T
timbu42 Themenstarter:in
31 Beiträge seit 2017
vor 5 Jahren

Autsch, ja das stimmt, einfach den Platzhalter auch Escapen, hätte ich auch drauf kommen können schäm

Danke! 😉