【问题标题】:Match occurrences of a character before a control character, match zero if control character not present匹配控制字符之前出现的字符,如果控制字符不存在则匹配零
【发布时间】:2013-04-13 11:23:59
【问题描述】:

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

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

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

TL;DR;

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

 (?<!\*.*)/

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

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

【问题讨论】:

  • 在调用正则表达式之前,您可以检查字符串中的星号吗? if(path.Contains("*")) DoRegex();
  • 当然。抱歉,应该说这更像是一个学术问题,看看它是否可以用一个表达式来完成。
  • 啊,我明白了 :) 有趣的问题。
  • Arghh,刚刚看到您追求的是更学术的答案,而不是问题的解决方案。好吧,我应该专注于那个:-(
  • 一切都很好@Renaud。 +1。我喜欢这个解决方案,它可能比纯正则表达式路线更好!谢谢。

标签: c# regex algorithm


【解决方案1】:

这是一种方法:

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

【讨论】:

    【解决方案2】:

    一种接近任务的方法:

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

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

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

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

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

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

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

    【讨论】:

      【解决方案3】:

      这里不需要正则表达式。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-29
        • 1970-01-01
        • 1970-01-01
        • 2011-09-01
        • 2014-05-09
        • 1970-01-01
        • 1970-01-01
        • 2011-05-26
        相关资源
        最近更新 更多