Необходимо выполнить поиск по строке с помощью Wildcard (*,? И т. Д.) С помощью Regex - PullRequest
58 голосов
/ 02 августа 2011

Мне нужно выполнить поиск по шаблону (*, ? и т. Д.) По строке.Это то, что я сделал:

string input = "Message";
string pattern = "d*";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);

if (regex.IsMatch(input))
{
    MessageBox.Show("Found");
}
else
{
    MessageBox.Show("Not Found");
}

С приведенным выше кодом блок «Найдено» ударяет, но на самом деле не должен!

Если мой шаблон «e *», то только «Найдено»«должен ударить.

Мое понимание или требование: d * поиск должен найти текст, содержащий« d », за которым следуют любые символы.

Должен ли я изменить свой шаблон на« d. * »и«е. * "?Есть ли какая-либо поддержка в .NET для Wild Card, которая внутренне это делает при использовании класса Regex?

Ответы [ 10 ]

114 голосов
/ 02 августа 2011

С http://www.codeproject.com/KB/recipes/wildcardtoregex.aspx:

public static string WildcardToRegex(string pattern)
{
    return "^" + Regex.Escape(pattern)
                      .Replace(@"\*", ".*")
                      .Replace(@"\?", ".")
               + "$";
}

Так что-то вроде foo*.xls? преобразуется в ^foo.*\.xls.$.

21 голосов
/ 24 мая 2013

Вы можете сделать простой подстановочный знак без RegEx, используя функцию Visual Basic с именем LikeString.

using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;

if (Operators.LikeString("This is just a test", "*just*", CompareMethod.Text))
{
  Console.WriteLine("This matched!");
}

Если вы используете CompareMethod.Text, она будет сравнивать без учета регистра.Для сравнения с учетом регистра вы можете использовать CompareMethod.Binary.

Подробнее здесь: http://www.henrikbrinch.dk/Blog/2012/02/14/Wildcard-matching-in-C

MSDN: http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.compilerservices.operators.likestring%28v=vs.100%29.ASPX

10 голосов
/ 26 апреля 2012

Правильная формулировка регулярного выражения для выражения glob d* равна ^d, что означает совпадение со всем, что начинается с d.

    string input = "Message";
    string pattern = @"^d";
    Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);

(в этом случае цитирование @ не является обязательным, но это хорошая практика, поскольку многие регулярные выражения используют escape-символы обратной косой черты, которые необходимо оставить в покое, и это также указывает читателю, что эта строка особенная).

6 голосов
/ 10 мая 2013

Windows и * nux обрабатывают подстановочные знаки по-разному.*, ? и . обрабатываются Windows очень сложным образом, чье присутствие или положение изменят смысл другого.В то время как * nux делает это простым, все, что он делает - это всего лишь одно простое сопоставление с образцом.Кроме того, Windows соответствует ? для 0 или 1 символа, Linux соответствует ему ровно для 1 символа.

Я не нашел авторитетных документов по этому вопросу, вот лишь мой вывод, основанный на днях испытанийWindows 8 / XP (командная строка, конкретная команда dir, а метод Directory.GetFiles также использует те же правила) и Ubuntu Server 12.04.1 (команда ls).Я заставил работать десятки распространенных и необычных дел, хотя есть и много неудачных дел.

Текущий ответ Гейба работает как * nux.Если вам также нужен стиль Windows, и вы готовы принять несовершенство, то вот оно:

    /// <summary>
    /// <para>Tests if a file name matches the given wildcard pattern, uses the same rule as shell commands.</para>
    /// </summary>
    /// <param name="fileName">The file name to test, without folder.</param>
    /// <param name="pattern">A wildcard pattern which can use char * to match any amount of characters; or char ? to match one character.</param>
    /// <param name="unixStyle">If true, use the *nix style wildcard rules; otherwise use windows style rules.</param>
    /// <returns>true if the file name matches the pattern, false otherwise.</returns>
    public static bool MatchesWildcard(this string fileName, string pattern, bool unixStyle)
    {
        if (fileName == null)
            throw new ArgumentNullException("fileName");

        if (pattern == null)
            throw new ArgumentNullException("pattern");

        if (unixStyle)
            return WildcardMatchesUnixStyle(pattern, fileName);

        return WildcardMatchesWindowsStyle(fileName, pattern);
    }

    private static bool WildcardMatchesWindowsStyle(string fileName, string pattern)
    {
        var dotdot = pattern.IndexOf("..", StringComparison.Ordinal);
        if (dotdot >= 0)
        {
            for (var i = dotdot; i < pattern.Length; i++)
                if (pattern[i] != '.')
                    return false;
        }

        var normalized = Regex.Replace(pattern, @"\.+$", "");
        var endsWithDot = normalized.Length != pattern.Length;

        var endWeight = 0;
        if (endsWithDot)
        {
            var lastNonWildcard = normalized.Length - 1;
            for (; lastNonWildcard >= 0; lastNonWildcard--)
            {
                var c = normalized[lastNonWildcard];
                if (c == '*')
                    endWeight += short.MaxValue;
                else if (c == '?')
                    endWeight += 1;
                else
                    break;
            }

            if (endWeight > 0)
                normalized = normalized.Substring(0, lastNonWildcard + 1);
        }

        var endsWithWildcardDot = endWeight > 0;
        var endsWithDotWildcardDot = endsWithWildcardDot && normalized.EndsWith(".");
        if (endsWithDotWildcardDot)
            normalized = normalized.Substring(0, normalized.Length - 1);

        normalized = Regex.Replace(normalized, @"(?!^)(\.\*)+$", @".*");

        var escaped = Regex.Escape(normalized);
        string head, tail;

        if (endsWithDotWildcardDot)
        {
            head = "^" + escaped;
            tail = @"(\.[^.]{0," + endWeight + "})?$";
        }
        else if (endsWithWildcardDot)
        {
            head = "^" + escaped;
            tail = "[^.]{0," + endWeight + "}$";
        }
        else
        {
            head = "^" + escaped;
            tail = "$";
        }

        if (head.EndsWith(@"\.\*") && head.Length > 5)
        {
            head = head.Substring(0, head.Length - 4);
            tail = @"(\..*)?" + tail;
        }

        var regex = head.Replace(@"\*", ".*").Replace(@"\?", "[^.]?") + tail;
        return Regex.IsMatch(fileName, regex, RegexOptions.IgnoreCase);
    }

    private static bool WildcardMatchesUnixStyle(string pattern, string text)
    {
        var regex = "^" + Regex.Escape(pattern)
                               .Replace("\\*", ".*")
                               .Replace("\\?", ".")
                    + "$";

        return Regex.IsMatch(text, regex);
    }

Забавно, даже Windows API PathMatchSpec не согласенс FindFirstFile .Просто попробуйте a1*., FindFirstFile говорит, что соответствует a1, PathMatchSpec говорит, что нет.

3 голосов
/ 15 апреля 2013

Вы должны экранировать специальные символы Regex во входном шаблоне подстановки (например, шаблон *.txt будет эквивалентен ^.*\.txt$) Поэтому косые черты, фигурные скобки и многие специальные символы должны быть заменены на @"\" + s, где s - специальный символ регулярного выражения.

3 голосов
/ 11 февраля 2013

Вам нужно преобразовать свое подстановочное выражение в регулярное выражение. Например:

    private bool WildcardMatch(String s, String wildcard, bool case_sensitive)
    {
        // Replace the * with an .* and the ? with a dot. Put ^ at the
        // beginning and a $ at the end
        String pattern = "^" + Regex.Escape(wildcard).Replace(@"\*", ".*").Replace(@"\?", ".") + "$";

        // Now, run the Regex as you already know
        Regex regex;
        if(case_sensitive)
            regex = new Regex(pattern);
        else
            regex = new Regex(pattern, RegexOptions.IgnoreCase);

        return(regex.IsMatch(s));
    } 
3 голосов
/ 02 августа 2011

d* означает, что он должен соответствовать нулю или более символов "d".Таким образом, любая строка является допустимым соответствием.Вместо этого попробуйте d+!

Для поддержки шаблонов подстановки я бы заменил символы подстановки эквивалентами RegEx.Как * становится .*, а ? становится .?.Тогда ваше выражение выше становится d.*

0 голосов
/ 11 апреля 2017

Я думаю, у @Dmitri есть хорошее решение Соответствие строк с подстановочными знаками https://stackoverflow.com/a/30300521/1726296

На основе его решения я создал два метода расширения. (ему достается кредит)

Может быть полезным.

public static String WildCardToRegular(this String value)
{
        return "^" + Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*") + "$";
}

public static bool WildCardMatch(this String value,string pattern,bool ignoreCase = true)
{
        if (ignoreCase)
            return Regex.IsMatch(value, WildCardToRegular(pattern), RegexOptions.IgnoreCase);

        return Regex.IsMatch(value, WildCardToRegular(pattern));
}

Использование:

string pattern = "file.*";

var isMatched = "file.doc".WildCardMatch(pattern)

или

string xlsxFile = "file.xlsx"
var isMatched = xlsxFile.WildCardMatch(pattern)
0 голосов
/ 26 февраля 2016

Вы можете использовать WildcardPattern из System.Management.Automation сборки. Смотрите мой ответ здесь .

0 голосов
/ 08 июля 2014

Весь верхний код неверен до конца.

Это потому, что при поиске zz * foo * или zz * вы не получите правильные результаты.

И если вы будете искать "abcd* "в" abcd "в TotalCommander он найдет файл abcd, поэтому весь верхний код неверен.

Вот правильный код.

public string WildcardToRegex(string pattern)
{             
    string result= Regex.Escape(pattern).
        Replace(@"\*", ".+?").
        Replace(@"\?", "."); 

    if (result.EndsWith(".+?"))
    {
        result = result.Remove(result.Length - 3, 3);
        result += ".*";
    }

    return result;
}
...