Подтвердить что ты не робот

Почему этот цикл через группы 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 ==

4b9b3361

Ответ 1

Это происходит потому, что группа ноль является специальной: она возвращает все совпадение.

Из Документация по регулярному выражению (выделено мной):

Простой шаблон регулярного выражения иллюстрирует, как пронумерованные (неназванные) и именованные группы могут ссылаться либо программно, либо с помощью синтаксиса языка выражения. Регулярное выражение ((?<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)

Если вы не хотите его видеть, запустите вывод из первой группы.

Ответ 2

Регулярное выражение захватывает сразу несколько групп. Группа 0 - это вся согласованная область (включая акценты). Группа 1 - это группа, определенная скобками.

Скажите, что ваше регулярное выражение имеет следующую форму:

A(B(C)D)E.

С выражениями A, B, C, D end E regex.

Затем будут сопоставлены следующие группы:

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

i -я группа начинается с i -й открытой скобки. И вы можете сказать, что "нулевая" открытая скобка неявно помещается в начало регулярного выражения (и заканчивается в конце регулярного выражения).

Если вы хотите опустить группу 0, вы можете использовать метод Skip структуры LINQ:

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

Ответ 3

0
"foobar123=="  --  Matched string. 

Полное совпадение по шаблону будет найдено в индексе 0.

1
foobar123==     -- Captured string. 

Групповой индекс 1 содержит символы, которые захватываются первой группой .

Ответ 4

Использование регулярного выражения @dasblinkenlight в качестве примера...

Это не вся история с подсчетом группы захвата Dot-Net. По мере добавления названных групп значение по умолчанию подсчитывается и считается последним. Они могут быть изменены.

Конечно, группа 0 всегда содержит весь матч. Групповой подсчет действительно начинается с 1
потому что вы не можете указать обратную ссылку (в регулярном выражении) группе 0, она конфликтует с с двоичной конструкцией \0000.

Здесь подсчитывается с именованными/нормальными группами в Dot-Net состояние по умолчанию.

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

Здесь с последними именами отключены.

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

Здесь, когда именованный счетчик выключен.

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

Ответ 5

Вы можете вернуть только один, удалив группу 1 с помощью ?:

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

Онлайн-демонстрация

Каждый раз, когда вы используете (), вы создаете группы, и вы можете ссылаться на них позже, используя обратные ссылки $1, $2, $3, конечно, в случае вашего выражения проще:

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

Что не использует скобки вообще