需要使用正则表达式对字符串执行通配符(*、?等)搜索

问题描述 投票:0回答:11

我需要对字符串执行通配符(

*
?
等)搜索。 这就是我所做的:

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*”,那么只有“Found”应该命中。

我的理解或要求是 d* 搜索应该找到包含“d”后跟任何字符的文本。

我应该将模式更改为“d.*”和“e.*”吗? .NET 中是否有对通配符的支持,它在使用 Regex 类时在内部执行此操作?

c# .net regex string wildcard
11个回答
124
投票

来自 http://www.codeproject.com/KB/recipes/wildcardtoregex.aspx

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

所以像

foo*.xls?
这样的东西会变成
^foo.*\.xls.$


22
投票

您可以使用名为 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


10
投票

glob 表达式

d*
的正确正则表达式公式是
^d
,这意味着匹配以
d
开头的任何内容。

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

(在这种情况下,不需要

@
引用,但这是很好的做法,因为许多正则表达式使用需要单独保留的反斜杠转义符,并且它还向读者表明该字符串是特殊的)。


8
投票

Windows 和 *nux 对待通配符的方式不同。

*
?
.
由Windows以非常复杂的方式处理,一个人的存在或位置会改变另一个人的含义。虽然 *nux 保持简单,但它所做的只是一种简单的模式匹配。除此之外,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
表示匹配
a1
PathMatchSpec
表示不匹配。


5
投票

d*
表示它应该匹配零个或多个“
d
”字符。所以任何字符串都是有效的匹配。试试
d+

为了支持通配符模式,我将用 RegEx 等效项替换通配符。就像

*
变成
.*
?
变成
.?
。那么你上面的表情就变成了
d.*


3
投票

您需要将通配符表达式转换为正则表达式。例如:

    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
投票

您必须转义输入通配符模式中的特殊正则表达式符号(例如模式

*.txt
将相当于
^.*\.txt$
) 因此斜杠、大括号和许多特殊符号必须替换为
@"\" + s
,其中
s
- 特殊正则表达式符号。


1
投票

您可能想使用 WildcardPattern

 程序集中的 
System.Management.Automation
。请参阅我的回答这里


1
投票

我认为@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
投票

所有上面的代码最终都不正确。

这是因为当搜索 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
投票

最受接受的答案适用于大多数情况,并且可以在大多数场景中使用:

"^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$";

但是,如果您允许在输入通配符模式时转义,例如“

find \*
”,意思是你想搜索带有星号的字符串“
find *
”,这是不行的。已经转义的*将转义为“
\\\\\\*
”,替换后我们得到“
^value\\ with\\\\.*$
”,这是错误的。

以下代码(肯定可以优化和重写)处理这种特殊情况:

  public static string WildcardToRegex(string wildcard)
    {
        var sb = new StringBuilder();
        for (var i = 0; i < wildcard.Length; i++)
        {
            // If wildcard has an escaped \* or \?, preserve it like it is in the Regex expression
            var character = wildcard[i];
            if (character == '\\' && i < wildcard.Length - 1)
            {
                if (wildcard[i + 1] == '*')
                {
                    sb.Append("\\*");
                    i++;
                    continue;
                }

                if (wildcard[i + 1] == '?')
                {
                    sb.Append("\\?");
                    i++;
                    continue;
                }
            }

            switch (character)
            {
                // If it's unescaped * or ?, change it to Regex equivalents. Add more wildcard characters (like []) if you need to support them.
                case '*':
                    sb.Append(".*");
                    break;
                case '?':
                    sb.Append('.');
                    break;
                default:
                    //// Escape all other symbols because wildcard could contain Regex special symbols like '.'
                    sb.Append(Regex.Escape(character.ToString()));
                    break;
            }
        }

        return $"^{sb}$";
    }

这里提出了仅使用正则表达式替换的问题解决方案https://stackoverflow.com/a/15275806/1105564

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.