c# - регулярные - RegEx-повторное использование подвыражений




регулярные выражения c# онлайн (4)

Допустим, у меня есть регулярное выражение, совпадающее с шестнадцатеричным 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 , но общий ответ тоже будет интересен.


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)
Рубин: ([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/


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

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

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

например

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

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

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


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

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

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


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

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

C # демо :

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-последовательности обрабатываются как комбинации литерального обратного слеша и символа после него. Обязательно определите литерал { с {{ и } с }} (например, [email protected]"(?:{block}){{5}}" чтобы повторить block 5 раз).

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

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

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





regex