为什么循环通过Regex组打印输出两次?

时间:2014-09-23 19:10:39

标签: c# regex

我写了这个非常简单的正则表达式代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace RegexTest1
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "\"foobar123==\"";
            Regex r = new Regex("^\"(.*)\"$");
            Match m = r.Match(a);
            if (m.Success)
            {
                foreach (Group g in m.Groups)
                {
                    Console.WriteLine(g.Index);
                    Console.WriteLine(g.Value);
                }
            }
        }
    }
}

然而输出是

0
"foobar123=="
1
foobar123==

我不明白为什么会打印两次。为什么在指数0处有一个捕获?当我在我的正则表达式^\"中说,我没有使用捕获。

很抱歉,如果这是非常基本的,但我不会每天都写Regex。

据我说,这段代码应该只打印一次,索引应该是1,值应该是foobar ==

5 个答案:

答案 0 :(得分:23)

这是因为组零是特殊的:它返回整个匹配。

Regex documentation(强调添加):

  

一个简单的正则表达式模式说明了如何以编程方式或使用正则表达式语言语法引用编号(未命名)和命名组。正则表达式((?<One>abc)\d+)?(?<Two>xyz)(.*)按编号和名称生成以下捕获组。 第一个捕获组(编号0)始终指整个模式。

#      Name              Group
- ---------------- --------------------------------
0 0 (default name) ((?<One>abc)\d+)?(?<Two>xyz)(.*)

1 1 (default name) ((?<One>abc)\d+)

2 2 (default name) (.*)

3 One (?<One>abc)

4 Two (?<Two>xyz)

如果您不想看到它,请从第一组开始输出。

答案 1 :(得分:13)

正则表达式一次捕获几个组。组0是整个匹配区域(包括重音符号)。组1是由括号定义的组。

说你的正则表达式有以下形式:

A(B(C)D)E.

使用ABCD结束E正则表达式。

然后将匹配以下组:

0 A(B(C)D)E
1 B(C)D
2 C

i组开始于i - 开放式括号。你可以说“零”开放括号隐式放在正则表达式的开头(并在正则表达式的末尾结束)。

如果要省略组0,可以使用 LINQ 框架的Skip方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace RegexTest1 {
    class Program {
        static void Main(string[] args) {
            string a = "\"foobar123==\"";
            Regex r = new Regex("^\"(.*)\"$");
            Match m = r.Match(a);
            if (m.Success) {
                foreach (Group g in m.Groups.Skip(1)) {//Skipping the first (thus group 0)
                    Console.WriteLine(g.Index);
                    Console.WriteLine(g.Value);
                }
            }
        }
    }
}

答案 2 :(得分:5)

0
"foobar123=="  --  Matched string. 

模式的完整匹配将在索引0处找到。

1
foobar123==     -- Captured string. 

组索引1包含第一个capturing group捕获的字符。

答案 3 :(得分:5)

使用@dasblinkenlight正则表达式作为示例......

这不是Dot-Net捕获组计数的全部故事。 添加命名组时,默认值会计算它们并最后计算它们。 这些可以选择改变。

当然,组0总是包含整个匹配。小组计数真的从1开始 因为你不能指定一个后向引用(在正则表达式中)到0组,它会发生冲突 使用二进制构造\0000

这里使用Dot-Net中的命名/普通组计数默认状态。

 (                                  # (1 start)
      (?<One> abc )                 #_(3)         
      \d+ 
 )?                                 # (1 end)
 (?<Two> xyz )                      #_(4)         
 ( .* )                             # (2)

这里的名字是最后一次 OFF

 (                                  # (1 start)
      (?<One> abc )                 # (2)
      \d+ 
 )?                                 # (1 end)
 (?<Two> xyz )                      # (3)
 ( .* )                             # (4)

这里的命名计数已转为 OFF

 (                                  # (1 start)
      (?<One> abc )
      \d+ 
 )?                                 # (1 end)
 (?<Two> xyz )
 ( .* )                             # (2)

答案 4 :(得分:2)

您可以使用?:

删除组1,只返回一个
 Regex r = new Regex("^\"(?:.*)\"$");

<强> Online Demo

每次使用()时,您都在创建群组,之后您可以使用反向引用$ 1,$ 2,$ 3来引用它们当然,如果您的表达式更简单,则会:

Regex r = new Regex("^\".*\"$");

根本没有使用括号