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

RegEx - повторное использование подвыражений

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

([0-9a-fA-F]{1,8})

Когда я создаю регулярное выражение, где мне нужно совместить это несколько раз, например

(?<from>[0-9a-fA-F]{1,8})\s*:\s*(?<to>[0-9a-fA-F]{1,8})

Должен ли я каждый раз повторять определение подвыражения, или есть способ "назвать и повторно использовать" его?

Я бы предположил что-то вроде (предупреждение, изобретенный синтаксис!)

(?<from>{hexnum=[0-9a-fA-F]{1,8}})\s*:\s*(?<to>{=hexnum})

где hexnum= будет определять подвыражение "hexnum", а {= hexnum} будет повторно использовать его.

Поскольку я уже понял, это важно: я использую .NET System.Text.RegularExpressions.Regex, но общий ответ тоже был бы интересным.

4b9b3361

Ответ 1

Подпрограммы RegEx

Если вы хотите использовать подвыражение несколько раз, не переписывая его, вы можете сгруппировать его, а затем вызвать его как подпрограмму. Подпрограммы могут вызываться по имени, индексу или относительной позиции.

Подпрограммы поддерживаются PCRE, Perl, Ruby, PHP, Delphi, R и другими. К сожалению,.NET Framework не хватает, но есть некоторые библиотеки PCRE для .NET, которые вы можете использовать вместо них (например, https://github.com/ltrzesniewski/pcre-net).

Синтаксис

Вот как работают подпрограммы: скажем, у вас есть суб-выражение [abc], которое вы хотите повторять три раза подряд.

Стандартный RegEx
Любое: [abc][abc][abc]

Подпрограмма, по имени
Perl:   (?'name'[abc])(?&name)(?&name)
PCRE: (?P<name>[abc])(?P>name)(?P>name)
Ruby: (?<name>[abc])\g<name>\g<name>

Подпрограмма по индексу
Perl/PCRE: ([abc])(?1)(?1)
Ruby:       ([abc])\g<1>\g<1>

Подпрограмма, по относительной позиции
Perl:   ([abc])(?-1)(?-1)
PCRE: ([abc])(?-1)(?-1)
Ruby: ([abc])\g<-1>\g<-1>

Подпрограмма, предопределенная
Это определяет подпрограмму без ее выполнения.
Perl/PCRE: (?(DEFINE)(?'name'[abc]))(?P>name)(?P>name)(?P>name)

Примеры

Соответствует допустимой строке адреса IPv4, от 0.0.0.0 до 255.255.255.255:
((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.(?1)\.(?1)\.(?1)

Без подпрограмм:
((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))

И для решения исходной проблемы:
(?<from>(?P<hexnum>[0-9a-fA-F]{1,8}))\s*:\s*(?<to>(?P>hexnum))

Подробнее

http://regular-expressions.info/subroutine.html
http://regex101.com/

Ответ 2

Почему бы не сделать что-то вроде этого, не совсем короче, но немного более ремонтопригодным.

String.Format("(?<from>{0})\s*:\s*(?<to>{0})", "[0-9a-zA-Z]{1,8}");

Если вы хотите больше кода для самостоятельного документирования, я бы присвоил строку с регулярным выражением числа с правильно названной константной переменной.

Ответ 3

Если я правильно понимаю ваш вопрос, вы хотите повторно использовать некоторые шаблоны для построения более крупного шаблона?

string f = @"fc\d+/";
string e = @"\d+";
Regex regexObj = new Regex(f+e);

Кроме этого, использование обратных ссылок поможет только в том случае, если вы пытаетесь сопоставить ту же самую строку, которую вы ранее сопоставили где-то в своем регулярном выражении.

например.

/\b([a-z])\w+\1\b/

Будет только соответствовать: text, spaces в приведенном выше тексте:

Это образец текста, который не является заголовком, так как он не заканчивается двумя пробелами.

Ответ 4

.NET regex не поддерживает рекурсию шаблонов, и если вы можете использовать (?<from>(?<hex>[0-9a-fA-F]{1,8}))\s*:\s*(?<to>(\g<hex>)) в Ruby и PHP/PCRE (где hex - это "техническая" группа захвата с именем, имя которой не должно встречаться в основном шаблоне), в .NET вы можете просто определить блок (-ы) как отдельные переменные, а затем использовать их для построения динамического шаблона.

Начиная с С# 6, вы можете использовать интерполированный строковый литерал, который очень похож на рекурсию подпрограммы PCRE/Onigmo, но на самом деле чище и не имеет потенциального узкого места, когда группа называется идентично "технической" группе захвата:

демонстрация С#:

using System;
using System.Text.RegularExpressions;

public class Test
{
    public static void Main()
    {
        var block = "[0-9a-fA-F]{1,8}";
        var pattern = [email protected]"(?<from>{block})\s*:\s*(?<to>{block})";
        Console.WriteLine(Regex.IsMatch("12345678  :87654321", pattern));
    }
}

[email protected]"..." - это строковый литерал, содержащий строковый интерполированный строковый литерал, где escape-последовательности обрабатываются как комбинация буквенного обратного слэша и char после него. Обязательно определите литерал { с помощью {{ и } с помощью }} (например, [email protected]"(?:{block}){{5}}", чтобы повторить block 5 раз).

Для более старых версий С# используйте string.Format:

var pattern = string.Format(@"(?<from>{0})\s*:\s*(?<to>{0})", block);

как предлагается в Маттиасе.

Ответ 5

Нет такого предопределенного класса. Я думаю, вы можете упростить его, используя опцию "игнорировать", например:

(?i)(?<from>[0-9a-z]{1,8})\s*:\s*(?<to>[0-9a-z]{1,8})

Ответ 6

Для повторного использования regex named capture group используйте этот синтаксис:\k <name> или \k'name '

Итак, ответ:

(?<from>[0-9a-fA-F]{1,8})\s*:\s*\k<from>

Дополнительная информация: http://www.regular-expressions.info/named.html