4

我正在研究允许用户为文件夹层次结构中的项目指定“通配符”路径以及当项目与该路径匹配时将执行的关联操作的功能。例如:

    Path         Action
    -----------  -------
 1. $/foo/*/baz  include
 2. $/foo/bar/*  exclude

现在有了上面的例子,一个项目 at$/foo/bar/baz将匹配这两个动作。鉴于此,我想提供通配符路径特异性的粗略分数,这将基于第一个通配符出现的“深度”。最深的路径将获胜。重要的是,只有*以正斜杠 ( /*/) 为界的通配符才允许使用(除非在末尾 then /*),并且可以在路径中的各个点指定任何数字。

TL;博士;

所以,我认为在第一个之前计算正斜杠数量的正则表达式*是要走的路。但是由于多种原因,如果路径中没有通配符,正斜杠的匹配将为零。我必须进行以下负面回顾:

 (?<!\*.*)/

当有通配符时效果很好(例如,上面的路径#1 有 2 个正斜杠匹配,#2 有 3 个正斜杠匹配),但是当没有通配符时,它自然匹配所有正斜杠。我确信这是一个简单的步骤,但由于生锈的正则表达式技能,我被卡住了。

理想情况下,从学术的角度来看,我想看看单个正则表达式是否可以捕捉到这一点,但是为更优雅的问题解决方案提供了加分!

4

3 回答 3

2

这将是一种方法:

match = Regex.Match(subject, 
    @"^       # Start of string
    (         # Match and capture in group number 1...
     [^*/]*   #  any number of characters except slashes or asterisks
     /        #  followed by a slash
    )*        # zero or more times.
    [^*/]*    # Match any additional non-slash/non-asterisk characters.
    \*        # Then match an asterisk", 
    RegexOptions.IgnorePatternWhitespace);

现在,如果字符串中没有星号subject(分数0),则此正则表达式将无法匹配。如果正则表达式匹配,您可以确定其中至少有一个星号。

现在的聪明之处在于 .NET 正则表达式,与大多数其他正则表达式风格不同,实际上可以计算重复捕获组匹配的次数(大多数其他正则表达式引擎只是丢弃该信息),这使我们能够确定斜杠之前的数量字符串中的第一个星号。

该信息可以在

match.Groups[1].Captures.Count

(当然这意味着“第一个星号之前没有斜线”和“根本没有星号”都会得到分数0,这似乎是你在问题中要求的,但我不确定为什么会这样说得通)

于 2013-04-20T06:45:31.373 回答
1

这里不需要正则表达式。

使用 LINQ,它是一个 2-liner:

string s = "$/foo/bar/baz";
var asteriskPos = s.IndexOf('*');  // will be -1 if there is no asterisk
var slashCount = s.Where((c, i) => c == '/' && i < asteriskPos).Count();
于 2013-04-20T03:58:56.617 回答
1

A way that would approach the task:

  1. Validate all test paths (make sure they are valid and contain either \*\ or end by *).

  2. Use a sorted collection to keep track of the test paths and associated actions.

  3. Sort the collection based on the position of the wildcard in the string.

  4. Test the item against each path in the sorted collection.
    You can replace the * in the string by .*? to use it in a regex.

  5. Stop at the first match and return the associated action, otherwise continue with the next test in the collection.

A quick test implementation of some of the above:

void Main()
{
    // Define some actions to test and add them to a collection
    var ActionPaths = new List<ActionPath>() {
        new ActionPath() {TestPath = "/foo/*/baz",   Action = "include"},
        new ActionPath() {TestPath = "/foo/bar/*",   Action = "exclude"},
        new ActionPath() {TestPath = "/foo/doo/boo", Action = "exclude"},
    };
    // Sort the list of actions based on the depth of the wildcard
    ActionPaths.Sort();

    // the path for which we are trying to find the corresponding action
    string PathToTest = "/foo/bar/baz";

    // Test all ActionPaths from the top down until we find something
    var found = default(ActionPath);
    foreach (var ap in ActionPaths) {
        if (ap.IsMatching(PathToTest)) {
            found = ap;
            break;
        }
    }

    // At this point, we have either found an Action, or nothing at all
    if (found != default(ActionTest)) {
        // Found an Action!
    } else {
        // Found nothing at all :-(
    }
}

// Hold and Action Test
class ActionPath : IComparable<ActionPath>
{
    public string TestPath;
    public string Action;

    // Returns true if the given path matches the TestPath
    public bool IsMatching(string path) {
        var t = TestPath.Replace("*",".*?");
        return Regex.IsMatch(path, "^" + t + "$");
    }

    // Implements IComparable<T>
    public int CompareTo(ActionPath other) {
       if (other.TestPath == null) return 1;
       var ia = TestPath.IndexOf("*");
       var ib = other.TestPath.IndexOf("*");
       if (ia < ib) return 1;       
       if (ia > ib) return -1;
       return 0;
   }
}
于 2013-04-20T01:22:21.730 回答