匹配控制字符前字符的出现次数,如果控制字符不存在则匹配零

时间:2013-04-20 00:25:14

标签: c# regex algorithm

我正致力于允许用户为文件夹层次结构中的项目指定“通配符”路径以及在项目与该路径匹配时将执行的相关操作。 e.g:

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

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

TL; DR;

所以,我认为在第一个*之前计算正斜杠数的正则表达式是要走的路。但是由于多种原因,在路径中没有通配符的情况下,正斜杠的匹配将为零。我有以下负面看法:

 (?<!\*.*)/

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

理想情况下,从学术角度来看,我想看看单个正则表达式是否可以捕获这个,但是为更优雅的解决方案提供了奖励积分!

3 个答案:

答案 0 :(得分: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,这似乎是你在问题中要求的,但是我不确定为什么这会有意义)

答案 1 :(得分:1)

一种接近任务的方法:

  1. 验证所有测试路径(确保它们有效且包含\*\或以*结尾)。

  2. 使用已排序的集合来跟踪测试路径和相关操作。

  3. 根据字符串中通配符的位置对集合进行排序。

  4. 针对已排序集合中的每个路径测试项目 您可以用*替换字符串中的.*?,以便在正则表达式中使用它。

  5. 在第一场比赛中停止并返回相关的操作,否则继续进行集合中的下一个测试。

  6. 上述部分内容的快速测试实现:

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

答案 2 :(得分:1)

这里不需要正则表达式。

使用LINQ,它是一个2线程:

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();