Übereinstimmende Zeichenfolgen mit Platzhalter

71

Ich möchte Zeichenfolgen mit einem Platzhalter (*) abgleichen, wobei der Platzhalter "any" bedeutet. Zum Beispiel:

*X = string must end with X
X* = string must start with X
*X* = string must contain X

Einige zusammengesetzte Verwendungen wie:

*X*YZ* = string contains X and contains YZ
X*YZ*P = string starts with X, contains YZ and ends with P.

Gibt es dafür einen einfachen Algorithmus? Ich bin mir nicht sicher, ob ich Regex verwenden soll (obwohl dies möglich ist).

Zur Verdeutlichung geben die Benutzer das Obige in ein Filterfeld ein (so einfach wie möglich). Ich möchte nicht, dass sie selbst reguläre Ausdrücke schreiben müssen. Etwas, das ich leicht aus der obigen Notation transformieren kann, wäre also gut.

Robinson
quelle
Sollte YZ ABC Xübereinstimmen *X*YZ*, dh müssen die Teilzeichenfolgen sowohl in der Zeichenfolge als auch im Muster in derselben Reihenfolge angezeigt werden oder nicht? Ich würde annehmen, dass es nicht übereinstimmen sollte, aber "Zeichenfolge enthält X und enthält YZ" macht es nicht klar. Wenn es übereinstimmen sollte, sind alle aktuellen Antworten falsch.
Bernhard Barker
Das wäre ein Nein. Im angegebenen Beispiel muss X vor YZ stehen.
Robinson

Antworten:

31

Nur zu Ihrer Information, Sie könnten den VB.NET Like-Operator verwenden :

string text = "x is not the same as X and yz not the same as YZ";
bool contains = LikeOperator.LikeString(text,"*X*YZ*", Microsoft.VisualBasic.CompareMethod.Binary);  

Verwenden CompareMethod.TextSie diese Option, wenn Sie den Fall ignorieren möchten.

Sie müssen hinzufügen using Microsoft.VisualBasic.CompilerServices;.

Tim Schmelter
quelle
hmm, das Hinzufügen von "using" führt zu:Type or namespace name 'CompilerServices' does not exist in namespace 'Microsoft.VisualBasic' (are you missing an assembly reference?
dylanh724
3
Sie müssen einen Verweis auf die Microsoft.VisualBasic.dll hinzufügen: stackoverflow.com/a/21212268/284240
Tim Schmelter
1
Es scheint, dass dies in .Net 4.6 nicht mehr verfügbar ist. :(
Andrew Rondeau
1
Ich benutze 4.7 und es funktioniert gut. Auf der Website wird darauf hingewiesen, dass dies in .NET Core- und .NET Standard-Projekten nicht unterstützt wird.
VoteCoffee
2
Es wird jetzt in .NET Core ab Version 3.0 unterstützt: docs.microsoft.com/en-us/dotnet/api/…
Holf
144

Platzhalter funktionieren häufig mit zwei Arten von Jokern:

  ? - any character  (one and only one)
  * - any characters (zero or more)

So können Sie diese Regeln leicht in geeignete reguläre Ausdrücke umwandeln :

  // If you want to implement both "*" and "?"
  private static String WildCardToRegular(String value) {
    return "^" + Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*") + "$"; 
  }

  // If you want to implement "*" only
  private static String WildCardToRegular(String value) {
    return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$"; 
  }

Und dann können Sie Regex wie gewohnt verwenden:

  String test = "Some Data X";

  Boolean endsWithEx = Regex.IsMatch(test, WildCardToRegular("*X"));
  Boolean startsWithS = Regex.IsMatch(test, WildCardToRegular("S*"));
  Boolean containsD = Regex.IsMatch(test, WildCardToRegular("*D*"));

  // Starts with S, ends with X, contains "me" and "a" (in that order) 
  Boolean complex = Regex.IsMatch(test, WildCardToRegular("S*me*a*X"));
Dmitry Bychenko
quelle
3
Tolle Lösung!
Cheshire Cat
Es ist nicht so einfach, wie Sie behaupten. Eine Besonderheit ist beispielsweise, dass bei Verwendung Directory.GetFilesauch eine Erweiterung mit drei Buchstaben .htmübereinstimmen würde .html, eine Erweiterung .aimit zwei Buchstaben jedoch nicht mit aixoder übereinstimmen würde aifg. Windows-Platzhalter sind auf den ersten Blick trivial, aber unter der Haube sind sie eine Reihe von älteren hyperkomplexen Regelsätzen.
Sebastian Mach
6
@ Sebastian Mach: Danke, dass du die Nuance erwähnt hast! Ich bin damit einverstanden , dass MS DOS (und Windows) Interpretation der Wildcards von Standard anders ist en.wikipedia.org/wiki/Wildcard_character jedoch die Frage nach Strings ist und es nicht erwähnt Dateien; Deshalb habe ich die einfachste Lösung gewählt, wenn ich davon ausgehe, dass *es sich um beliebige Zeichen (null oder mehr) und ?genau um ein Zeichen handelt.
Dmitry Bychenko
1
Die ursprüngliche Frage lautete, ob Zeichenfolgenbezeichner und nicht das Dateisystem korrekt sind.
Robinson
8
Wenn Sie sich Gedanken über die Leistung machen, finden Sie hier eine C # -Implementierung eines Wildcard-Matching-Algorithmus, der für dieses spezielle Problem viel schneller als RegEx ist.
Dan
19

Die Verwendung von WildcardPatternvon System.Management.Automationkann eine Option sein.

pattern = new WildcardPattern(patternString);
pattern.IsMatch(stringToMatch);

In der Visual Studio-Benutzeroberfläche können Sie möglicherweise keine System.Management.AutomationAssembly zu Referenzen Ihres Projekts hinzufügen . Fühlen Sie sich frei, es manuell hinzuzufügen, wie hier beschrieben .

VirtualVDX
quelle
6

Ein Platzhalter *kann übersetzt werden .*oder .*?RegexMuster.

Möglicherweise müssen Sie einen Singleline-Modus verwenden, um Newline-Symbole abzugleichen. In diesem Fall können Sie ihn (?s)als Teil des Regex-Musters verwenden.

Sie können es für das gesamte oder einen Teil des Musters festlegen:

X* = > @"X(?s:.*)"
*X = > @"(?s:.*)X"
*X* = > @"(?s).*X.*"
*X*YZ* = > @"(?s).*X.*YZ.*"
X*YZ*P = > @"(?s:X.*YZ.*P)"
Wiktor Stribiżew
quelle
Sie sind nicht in jedem Fall gleichwertig. Beispielsweise stimmt *.htmauch ein Windows-Platzhalter überein *.html.
Sebastian Mach
5

*X*YZ* = string contains X and contains YZ

@".*X.*YZ"

X*YZ*P = string starts with X, contains YZ and ends with P.

@"^X.*YZ.*P$"
Avinash Raj
quelle
OK, mit Regex gibt es nichts, durch das ich einfach * ersetzen kann, um das zu bekommen, was ich will? Diese Abfragen werden von Benutzern ausgeführt, und ich erwarte nicht, dass sie reguläre Ausdrücke verstehen.
Robinson
Ja, aber Anfang und Ende müssen Sie Anker angeben. ^Start, $Ende
Avinash Raj
OK, danke Avinash. Es ist dann nur * durch. * Für den regulären Ausdruck zu ersetzen.
Robinson
1
Diese Antwort braucht wirklich mehr Erklärungen.
Jerther
5

Es ist zu berücksichtigen, dass Regex IsMatch mit XYZ wahr ist, wenn die Übereinstimmung mit Y * überprüft wird. Um dies zu vermeiden, verwende ich den Anker "^"

isMatch(str1, "^" + str2.Replace("*", ".*?"));  

Der vollständige Code zur Lösung Ihres Problems lautet also

    bool isMatchStr(string str1, string str2)
    {
        string s1 = str1.Replace("*", ".*?");
        string s2 = str2.Replace("*", ".*?");
        bool r1 = Regex.IsMatch(s1, "^" + s2);
        bool r2 = Regex.IsMatch(s2, "^" + s1);
        return r1 || r2;
    }
Pavel Khrapkin
quelle
2
Willkommen bei Stack Overflow! Während Sie möglicherweise das Problem des Fragestellers gelöst haben, sind reine Code-Antworten für andere, die auf diese Frage stoßen, nicht sehr hilfreich. Bitte bearbeiten Sie Ihre Antwort, um zu erklären, warum Ihr Code das ursprüngliche Problem löst.
Joe C
Diese Lösung würde funktionieren, wenn Sie einfach alpanumerische Zeichen und einige andere Zeichen abgleichen. Sie würde jedoch fehlschlagen, wenn Sie versuchen würden, ein anderes Zeichen abzugleichen, das die Syntax des regulären Ausdrucks definiert, z. B. " / " oder " [ ". nur als ein paar Beispiele.
Jimmyfever
-3

Anwendungsbeispiel für die C # -Konsole

Befehlszeile Beispiel:
C: /> App_Exe -Opy PythonFile.py 1 2 3
Konsolenausgabe:
Argumentliste: -Opy PythonFile.py 1 2 3
Gefundener Python-Dateiname: PythonFile.py

using System;
using System.Text.RegularExpressions;           //Regex

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string cmdLine = String.Join(" ", args);

            bool bFileExtFlag = false;
            int argIndex = 0;
            Regex regex;
            foreach (string s in args)
            {
                //Search for the 1st occurrence of the "*.py" pattern
                regex = new Regex(@"(?s:.*)\056py", RegexOptions.IgnoreCase);
                bFileExtFlag = regex.IsMatch(s);
                if (bFileExtFlag == true)
                    break;
                argIndex++;
            };

            Console.WriteLine("Argument list: " + cmdLine);
            if (bFileExtFlag == true)
                Console.WriteLine("Found python filename: " + args[argIndex]);
            else
                Console.WriteLine("Python file with extension <.py> not found!");
        }


    }
}
geo le
quelle
Sie lösen also ein Problem mit einer externen Anwendung? Ist Ihnen klar, wie viele nicht benötigte Ressourcen verschwendet werden?
NucS
@NucS Ich denke, wir sollten den Code analysieren und herausfinden, was nützlich ist. Jedenfalls sehe ich nicht, was dies über andere Antworten bringt.
Jerther