我需要对字符串执行通配符(
*
、?
等)搜索。
这就是我所做的:
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 类时在内部执行此操作?
来自 http://www.codeproject.com/KB/recipes/wildcardtoregex.aspx:
public static string WildcardToRegex(string pattern)
{
return "^" + Regex.Escape(pattern)
.Replace(@"\*", ".*")
.Replace(@"\?", ".")
+ "$";
}
所以像
foo*.xls?
这样的东西会变成 ^foo.*\.xls.$
。
您可以使用名为 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
glob 表达式
d*
的正确正则表达式公式是 ^d
,这意味着匹配以 d
开头的任何内容。
string input = "Message";
string pattern = @"^d";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
(在这种情况下,不需要
@
引用,但这是很好的做法,因为许多正则表达式使用需要单独保留的反斜杠转义符,并且它还向读者表明该字符串是特殊的)。
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
表示不匹配。
d*
表示它应该匹配零个或多个“d
”字符。所以任何字符串都是有效的匹配。试试d+
!
为了支持通配符模式,我将用 RegEx 等效项替换通配符。就像
*
变成 .*
,?
变成 .?
。那么你上面的表情就变成了d.*
您需要将通配符表达式转换为正则表达式。例如:
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));
}
您必须转义输入通配符模式中的特殊正则表达式符号(例如模式
*.txt
将相当于^.*\.txt$
)
因此斜杠、大括号和许多特殊符号必须替换为 @"\" + s
,其中 s
- 特殊正则表达式符号。
我认为@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)
所有上面的代码最终都不正确。
这是因为当搜索 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;
}
最受接受的答案适用于大多数情况,并且可以在大多数场景中使用:
"^" + 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