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

本文关键字:控制字符 如果 不存在 字符 | 更新日期: 2023-09-27 18:10:01

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

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

在上面的例子中,位于$/foo/bar/baz的物品将匹配这两个动作。考虑到这一点,我想提供通配符路径特异性的粗略分数,这将基于第一个通配符出现的"深度"。最具深度的路径将获胜。重要的是,只有以正斜杠(/*/)为界的*被允许作为通配符(除非在末尾然后是/*),并且可以在路径的各个点指定任何数字。

TL;博士;

所以,我认为在第一个*之前计算正斜杠数量的正则表达式是可行的。但是,由于多种原因,当路径中没有通配符时,正斜杠的匹配将为零。我得到了以下消极的回顾:

 (?<!'*.*)/

在有通配符的情况下工作得很好(例如2个斜杠匹配上面的路径#1,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

(当然这意味着"在第一个星号之前没有斜杠"answers"根本没有星号"都会得到分数0,这似乎是你在你的问题中所要求的,但我不确定为什么这会有意义)

处理任务的方法:

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

这里不需要正则表达式。

对于LINQ,它是一个双行:

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