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

C# 在控制字符之前匹配字符的出现次数,如果控制字符不存在,则匹配零,c#,regex,algorithm,C#,Regex,Algorithm,我正在开发允许用户为文件夹层次结构中的项目指定“通配符”路径的功能,以及在项目匹配该路径时将执行的关联操作。e、 g: Path Action ----------- ------- 1. $/foo/*/baz include 2. $/foo/bar/* exclude 现在通过上面的示例,位于$/foo/bar/baz的项目将匹配这两个操作。鉴于此,我想提供通配符路径的特异性的粗略分数,它将基于第一个通配符出现的“深度”。深度最大的路径将获胜。重

我正在开发允许用户为文件夹层次结构中的项目指定“通配符”路径的功能,以及在项目匹配该路径时将执行的关联操作。e、 g:

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

TL;博士

因此,我认为在第一个
*
之前,使用正则表达式来计算正斜杠的数量是一种可行的方法。但是,由于许多原因,如果路径中没有通配符,则正向斜杠的匹配将为零。我有以下负面回顾:

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


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

一种完成任务的方法:

  • 验证所有测试路径(确保它们有效并包含
    \*\
    或以
    *
    结尾)

  • 使用已排序的集合跟踪测试路径和相关操作

  • 根据通配符在字符串中的位置对集合进行排序

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

  • 在第一次匹配时停止并返回关联的操作,否则继续集合中的下一个测试

  • 以上部分的快速测试实现:

    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;
       }
    }
    
    void Main()
    {
    //定义一些要测试的操作并将其添加到集合中
    var actionpath=新列表(){
    新建ActionPath(){TestPath=“/foo/*/baz”,Action=“include”},
    新建ActionPath(){TestPath=“/foo/bar/*”,Action=“exclude”},
    新建ActionPath(){TestPath=“/foo/doo/boo”,Action=“exclude”},
    };
    //根据通配符的深度对操作列表进行排序
    actionpath.Sort();
    //我们试图找到相应操作的路径
    字符串PathToTest=“/foo/bar/baz”;
    //自上而下测试所有ActionPath,直到找到一些东西
    var found=默认值(ActionPath);
    foreach(ActionPath中的var ap){
    if(ap.IsMatching(路径测试)){
    发现=ap;
    打破
    }
    }
    //在这一点上,我们要么找到了一个动作,要么什么都没有
    如果(找到!=默认值(ActionTest)){
    //找到一个动作!
    }否则{
    //什么也没找到:-(
    }
    }
    //保持和动作测试
    类ActionPath:IComparable
    {
    公共字符串测试路径;
    公共字符串操作;
    //如果给定路径与TestPath匹配,则返回true
    公共布尔值匹配(字符串路径){
    var t=TestPath.Replace(“*”,“*?”);
    返回Regex.IsMatch(路径“^”+t+“$”);
    }
    //实现不可比较
    公共整数比较(ActionPath其他){
    if(other.TestPath==null)返回1;
    var ia=TestPath.IndexOf(“*”);
    var ib=other.TestPath.IndexOf(“*”);
    如果(iaib)返回-1;
    返回0;
    }
    }
    
    这里不需要正则表达式

    使用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();
    
    string s=“$/foo/bar/baz”;
    var asteriskPos=s.IndexOf('*');//如果没有星号,则为-1
    var slashCount=s.Where((c,i)=>c='/'&&i
    这是一种方法:

    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
    ,这似乎是您在问题中所要求的,但我不确定为什么这会有意义)

    在调用正则表达式之前,您能检查字符串中是否有星号吗?
    如果(path.Contains(“*”)DoRegex();
    当然。对不起,我应该说这更像是一个学术问题,看看是否可以用一个表达式来表达。啊,我明白了:)这是一个有趣的问题。啊,刚才看到你想要的是一个更学术的答案,而不是问题的解决方案。好吧,我应该集中精力:-(这一切都很好@Renaud.+1.我喜欢这个解决方案,它可能比纯regex路线要好!谢谢。