web-dev-qa-db-ja.com

Regexを使用して文字列でワ​​イルドカード(* 、?など)検索を実行する必要がある

ワイルドカード(*?など)文字列で検索します。これは私がやったことです:

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");
}

上記のコードでは「Found」ブロックがヒットしていますが、実際にはヒットしません!

パターンが「e *」の場合、「Found」のみがヒットします。

私の理解または要件はd *検索です。「d」の後に文字が続くテキストを検索する必要があります。

パターンを「d。*」および「e。*」として変更する必要がありますか?正規表現クラスを使用しているときに内部的にサポートするワイルドカードの.NETでのサポートはありますか?

59
Scott

http://www.codeproject.com/KB/recipes/wildcardtoregex.aspx から:

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

したがって、foo*.xls?のようなものは^foo.*\.xls.$に変換されます。

115
Gabe

LikeStringと呼ばれるVisual Basic関数を使用して、RegExなしで単純なワイルドカードマッハを実行できます。

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

21
Adam Szabo

グロブ式d*の正しい正規表現の定式化は^dであり、これはdで始まるものすべてに一致することを意味します。

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

(この場合、@引用符は必要ありませんが、多くの正規表現はそのままにする必要があるバックスラッシュエスケープを使用するため、良い習慣であり、この文字列が特別であることも読者に示します)。

10
Mark Lakata

Windowsと* nuxでは、ワイルドカードの扱いが異なります。 *?および.はWindowsによって非常に複雑な方法で処理されます。ある人の存在や位置は別の人の意味を変えるでしょう。 * nuxはシンプルに保ちますが、行うのは1つの単純なパターンマッチだけです。それに加えて、Windowsは?は、0文字または1文字の場合、Linuxは正確に1文字に一致します。

この問題に関する信頼できるドキュメントは見つかりませんでした。ここでは、Windows 8/XPでのテストの日数に基づいた結論のみを示します(コマンドライン、特定のdirコマンド、およびDirectory.GetFilesメソッドも同じルールを使用します)およびUbuntu Server 12.04.1(lsコマンド)。多くの失敗したケースもありますが、私は数十の一般的なケースと珍しいケースを機能させました。

Gabeによる現在の答えは、* 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は、一致することを示していますa1PathMatchSpecはそうではありません。

7
deerchao

d*は、ゼロ個以上の「d」文字と一致する必要があることを意味します。したがって、任意の文字列は有効な一致です。代わりにd+を試してください!

ワイルドカードパターンをサポートするために、ワイルドカードを正規表現に置き換えます。 *.*になり、?.?になります。すると、上の式はd.*になります

3
Anders Zommarin

ワイルドカード表現を正規表現に変換する必要があります。例えば:

    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
carlos357

入力ワイルドカードパターンで特殊な正規表現記号をエスケープする必要があります(たとえば、パターン*.txt^.*\.txt$と同等です)、スラッシュ、ブレース、および多くの特殊記号は@"\" + sで置き換える必要があります。ここでs-special正規表現のシンボル。

3
Camarada

@Dmitriにはワイルドカードを使用した文字列のマッチングでいい解決策があると思います https://stackoverflow.com/a/30300521/1726296

彼のソリューションに基づいて、2つの拡張メソッドを作成しました。 (クレジットは彼に行く)

役立つかもしれません。

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)
1
Tejasvi Hegde

すべての上位コードは最後まで正しくありません。

これは、zz * foo *またはzz *を検索すると、正しい結果が得られないためです。

TotalCommanderの「abcd」で「abcd *」を検索すると、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;
}
0
user3816530

WildcardPattern from System.Management.Automationアセンブリ。私の答えをご覧ください こちら

0
VirtualVDX