最多匹配组中的组一次

时间:2014-05-17 08:29:18

标签: .net regex

我想使用正则表达式来验证类似这样的字符串:

foo.bar=123.baz=456.qux=789.urr

(其中123456789是占位符的任意数字,但是foobarbaz,{{1 }和qux是文字文字,而不是占位符。我需要单独捕获urr / bar / baz的值,以便我使用命名捕获组

问题是quxbar=abaz=b完全是可选的,可以按任何顺序出现。所以我的正则表达式需要接受这些有效:

qux=c

...但拒绝这些无效:

foo.urr
foo.bar=123.urr
foo.qux=123.bar=456.urr
foo.baz=123.bar=456.qux=789.urr

我的正则表达式代码现在是这样的:

foo.bar=123.bar=456.urr
foo.qux=123.baz=456.qux=789.urr

我的表达式使用const String Bar = @"(?<bar>bar=(\d)+)"; const String Baz = @"(?<baz>baz=(\d)+)"; const String Qux = @"(?<qux>qux=(\d)+)"; const String Regex = @"^foo\.((" + Bar + "|" + Baz + "|" + Qux + @")\.)*urr$"; 以任意顺序允许可选的*BarBaz命名组,但它也允许它们多次出现,不应该被允许。

我知道我可以用Qux表达式来强制它,但是将来这些元素的数量会随着时间的推移而扩展,并且无法进行硬编码。

更新

我可以在原始问题中使用更好的示例,但n!barbaz的值都有自己的子表达式,例如qux接受十六进制数字,bar接受十进制数字,baz仅接受字母字符,因此无效:

qux

2 个答案:

答案 0 :(得分:1)

您可以使用以下正则表达式:

^foo(?:\.(?<name>bar|baz|qux)=(?<value>\d+)(?!.*\k<name>))*.urr$

Regular expression visualization

示例代码:

string[] lines = {
    "foo.urr",
    "foo.bar=123.urr",
    "foo.qux=123.bar=456.urr",
    "foo.baz=123.bar=456.qux=789.urr",
    "foo.bar=123.bar=456.urr",
    "foo.qux=123.baz=456.qux=789.urr"
};
foreach (string line in lines)
{
    Match m = Regex.Match(line, @"^foo(?:\.(?<name>bar|baz|qux)=(?<value>\d+)(?!.*\k<name>))*.urr$");
    Console.WriteLine("{0} : {1}", line, m.Success);
    if (m.Success)
    {
        for (int i = 0; i < m.Groups["name"].Captures.Count; i++)
            Console.WriteLine("{0} = {1}",
                              m.Groups["name"].Captures[i].Value,
                              m.Groups["value"].Captures[i].Value);
    }
}

示例输出:

foo.urr : True
foo.bar=123.urr : True
bar = 123
foo.qux=123.bar=456.urr : True
qux = 123
bar = 456
foo.baz=123.bar=456.qux=789.urr : True
baz = 123
bar = 456
qux = 789
foo.bar=123.bar=456.urr : False
foo.qux=123.baz=456.qux=789.urr : False

<强>更新

每个小组都必须有一个名字。然后我们可以做出以下原则:如果给出A,B,C,则ABC重复3次将给出所有可能的组合,即(A?B?C?){3}。然后我们只需要负向前瞻,这样如果它已经匹配就没有其他匹配的A.

^foo(?:(?:.bar=(?<bar>\d+)(?!.*\.bar))?(?:.baz=(?<baz>\d+)(?!.*\.baz))?(?:.qux=(?<qux>\d+)(?!.*\.qux))?){0,3}.urr$

Regular expression visualization

示例代码:

string[] lines = {
    "foo.urr",
    "foo.bar=123.urr",
    "foo.qux=123.bar=456.urr",
    "foo.baz=123.bar=456.qux=789.urr",
    "foo.bar=123.bar=456.urr",
    "foo.qux=123.baz=456.qux=789.urr"
};
foreach (string line in lines)
{
    Match m = Regex.Match(line, @"^foo((?:.bar=(?<bar>\d+)(?!.*\.bar))?(?:.baz=(?<baz>\d+)(?!.*\.baz))?(?:.qux=(?<qux>\d+)(?!.*\.qux))?){0,3}.urr$");
    Console.WriteLine("{0} : {1}", line, m.Success);
    if (m.Success)
    {
        Group bar = m.Groups["bar"];
        if (bar.Success)
            Console.WriteLine("bar = {0}", bar.Value);
        Group baz = m.Groups["baz"];
        if (baz.Success)
            Console.WriteLine("baz = {0}", baz.Value);
        Group qux = m.Groups["qux"];
        if (qux.Success)
            Console.WriteLine("qux = {0}", qux.Value);
    }
}

输出:

foo.urr : True
foo.bar=123.urr : True
bar = 123
foo.qux=123.bar=456.urr : True
bar = 456
qux = 123
foo.baz=123.bar=456.qux=789.urr : True
bar = 456
baz = 123
qux = 789
foo.bar=123.bar=456.urr : False
foo.qux=123.baz=456.qux=789.urr : False

答案 1 :(得分:0)

正如您所要求的示例,这是一种以正确格式捕获所有字符串的方法:

^foo\.(?:([a-zA-Z]+)\=(\d+)\.)*urr$

有两个编号的捕获组,因此在您的示例foo.baz=123.bar=456.qux=789.urr中,匹配将如下所示:

0: baz, bar, qux
1: 123, 456, 789

在代码中处理此问题相对简单。例如:

var reg = new Regex(@"^foo\.(?:([a-zA-Z]+)\=(\d+)\.)*urr$");
var test = "foo.baz=123.bar=456.qux=789.urr";
var match = reg.Match(test);
return match.Groups[0].Captures.Count == match.Groups[0].Captures.Cast<Capture>().Distinct();

如果没有您描述的重复字符串,则返回true,否则返回false。

作为代码处理的另一个例子,您还可以很容易地将两个捕获集合转换为使用所描述技术的字典,例如:create a dictionary using 2 lists using LINQ

另请注意,如果要在正则表达式中指定键,可以将[a-zA-Z]+替换为要匹配的单个字符串,如baz|bar|qux。或者,您也可以在代码中检查它。