我正在尝试查找目录中是否至少有一个文件与模式匹配(只使用"?"one_answers"*"通配符),但某些组合不断抛出嵌套限定符错误。例如,TestCashFile_10_12-25-2016????????.c??
不起作用。
这些模式来自非技术用户(他们受过这两个通配符基本用法的教育),所以"?"one_answers"*"几乎可以放在文件名的任何地方,我没有太多控制权。
这些图案出了什么问题?
这是运行此regex-的C#代码片段
string fileName = C:TestFilesTestCashFile_10_12-25-2016????????.c??'
string directory = Path.GetDirectoryName(fileName);
string[] temp = fileName.Split('\');
string file = temp[temp.Length - 1];
var found = Directory.GetFiles(directory).Any(p => Regex.Match(p, file).Success);
更新-这个问题已经解决,但如果它能帮助其他人寻找类似的东西,只是为了澄清-在这种情况下,我想用"?"来表示必须只有一个元素(而不是零或一个元素)。
如果您需要"??"来匹配任意字符中的两个,那么您是对的,您必须使用正则表达式。文件系统通配符将"?"视为"零或任意字符之一"。
但是您不能按照您尝试的方式来做,因为您在向用户请求文件系统通配符——您只是稍微改变了语义。您必须将用户的字符串转换为您想要的正则表达式:
a???.*
必须成为
a.?.?.?..*
- 每个问号都变成".":任何字符中的一个
- 每个"."都变成了".",因为未转义的"."是正则表达式中的一个特殊字符
- 每个">"都必须变成".":任何字符的零个或多个(猜测这个)
对file
字符串执行此操作,您的.Any(p => Regex.Match(p, file).Success);
应该可以工作。
不过,如果运行时速度有点慢,您可能需要编译regex:
file = TranslateWildcardsToRegex(file);
var re = new Regex(file);
var found = Directory.GetFiles("").Any(p => re.IsMatch(p));
我认为这对TranslateWildcardsToRegex()
:来说是正确的
public static String TranslateWildcardsToRegex(String s)
{
StringBuilder sb = new StringBuilder();
foreach (var ch in s)
{
switch (ch)
{
case '?':
sb.Append(".");
break;
case '*':
sb.Append(".*");
break;
// Escape a variety of characters that
// mean something special in a regex
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case '.':
sb.Append("\" + ch);
break;
default:
sb.Append(ch);
break;
}
}
return sb.ToString();
}
更新
在评论中@spender提供了一种更好、更干净的方式来做同样的事情:
var reStr = Regex.Escape(someWildcardThing).Replace(@"?", ".").Replace(@"*", ".*")
这么多年过去了,除了仍然是一个正在恢复的C程序员之外,我没有任何理由不这样做。
?
运算符指定前一个元素可以出现0或1次。
https://msdn.microsoft.com/en-us/library/az24scfc(v=vs.110).aspx
?匹配上一个元素零次或一次。"rai?n"ran"rain">
如果你像@Ed Plunkett所说的那样使用Directory.GetFiles
中内置的通配符,它的工作原理应该与你想要的类似。
如果你仍然想使用当前的RegEx方法,请执行以下操作:
.*
-任意数量的字符.{n}
-将n
替换为所需的字符数.{m,n}
-将m
替换为期望的最小字符数,将n替换为期望字符的最大字符数