Что такое группа без захвата? Что делает (?:) делать?

Как ?: используется и для чего он нужен?

14 ответов


позвольте мне попытаться объяснить это на примере.

рассмотрим следующий текст:

http://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex

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

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

... Я бы получил следующий результат:

Match "http://stackoverflow.com/"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "https"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"

но меня не волнует протокол - мне просто нужен хост и путь URL-адреса. Итак, я изменяю регулярное выражение, чтобы включить группу без захвата (?:).

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

Итак, мой результат выглядит так это:

Match "http://stackoverflow.com/"
     Group 1: "stackoverflow.com"
     Group 2: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"

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


EDIT:

как просили, позвольте мне попытаться объяснить группы тоже.

ну, группы служат многим целям. Они могут помочь вам извлечь точную информацию из большего совпадения (которое также может быть названо), они позволяют вам сопоставить предыдущую сопоставленную группу и могут использоваться для подстановок. Давайте попробовать некоторые примеры, мы должны?

хорошо, представьте, что у вас есть какой-то XML или HTML (имейте в виду, что regex не может быть лучшим инструментом для работы, но это хорошо, как пример). Вы хотите разобрать теги, чтобы вы могли сделать что-то вроде этого (я добавил пробелы, чтобы было легче понять):

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\>

первое регулярное выражение имеет именованную группу (тег), а второе-общую группу. Оба regexes делают то же самое: они используют значение с первого группа (имя тега), соответствующая закрывающему тегу. Разница в том, что первый использует имя для соответствия значению, а второй-групповой индекс (который начинается с 1).

давайте попробуем некоторые замены сейчас. Рассмотрим следующий текст:

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.

теперь, давайте использовать это тупое выражение на это:

\b(\S)(\S)(\S)(\S*)\b

это регулярное выражение соответствует словам не менее 3 символов и использует группы для разделения первых трех букв. Результат это:

Match "Lorem"
     Group 1: "L"
     Group 2: "o"
     Group 3: "r"
     Group 4: "em"
Match "ipsum"
     Group 1: "i"
     Group 2: "p"
     Group 3: "s"
     Group 4: "um"
...

Match "consectetuer"
     Group 1: "c"
     Group 2: "o"
     Group 3: "n"
     Group 4: "sectetuer"
...

Итак, если мы применяем подстановки...

__

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

L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.

вы также можете использовать именованные группы для подстановок, используя ${name}.

чтобы играть с regexes, я рекомендую http://regex101.com/, который предлагает хорошее количество деталей о том, как работает регулярное выражение; он также предлагает несколько движков регулярных выражений на выбор.


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

скажем, вы хотите сопоставить числовой текст, но некоторые числа могут быть записаны как 1 - й, 2-й, 3-й, 4-й,... Если вы хотите захватить числовую часть, но не (необязательный) суффикс, вы можете использовать группу без захвата.

([0-9]+)(?:st|nd|rd|th)?

, что будет соответствовать цифры в форме 1, 2, 3... или в форме 1 - й, 2-й, 3-й,... но он будет захватывать только числовую часть.


?: используется, когда вы хотите сгруппировать выражение, но не хотите сохранять его как согласованную/захваченную часть строки.

примером может быть то, что соответствует IP-адресу:

/(?:\d{1,3}\.){3}\d{1,3}/

обратите внимание, что я не забочусь о сохранении первых 3 октетов, но (?:...) группировка позволяет мне сократить регулярное выражение без накладных расходов на захват и хранение совпадения.


это делает группу без захвата, что означает, что подстрока, сопоставленная этой группе, не будет включена в список захватов. Пример в Ruby, чтобы проиллюстрировать разницу:

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]

историческая мотивация: существование групп без захвата можно объяснить с помощью скобок. Рассмотрим выражения (a|b)c и a | bc, из-за приоритета конкатенации над/, эти выражения представляют два разных языка ({ac, bc} и {a, bc} соответственно). Однако скобки также используются в качестве соответствующей группы (как объясняется в других ответах...).

когда вы хотите иметь скобки, но не захватывать подвыражение, которое вы используете без захвата ГРУППЫ. В Примере (?: a / b)c


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

группы без захвата велики, если вы пытаетесь захватить много разных вещей, и есть некоторые группы, которые вы не хотите захватывать.

это в значительной степени причина их существования. В то время как вы узнаете о группах, узнайте о Атомные Группы они много делают! Есть также lookaround группы, но они немного сложнее и не используются так много.

пример использования позже в регулярном выражении (backreference):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</> [ находит xml-тег (без поддержки ns) ]

([A-Z][A-Z0-9]*) является группой захвата (в этом случае это tagname)

позже в регулярном выражении что означает, что он будет только соответствовать тот же текст, который был в первой группе (([A-Z][A-Z0-9]*) group) (в этом случае он соответствует тегу end).


давай попробуем на примере :-

Код Регулярного Выражения: -(?:animal)(?:=)(\w+)(,)

Строка Поиска :-

строка 1 - animal=cat,dog,cat,tiger,dog

строка 2 - animal=cat,cat,dog,dog,tiger

строка 3 - animal=dog,dog,cat,cat,tiger

(?:animal) --> Non-Captured Group 1

(?:=)--> Non-Captured Group 2

(\w+)--> Захваченная Группа 1

(,)--> Захваченная Группа 2

--> результат захваченной группы 1 i.e В строке 1-Кошка,в строке 2-Кошка, в строке 3-собака.

-->результат захваченной группы 2 i.e запятая(,)

поэтому в этом коде, давая \1 и \2, мы вспоминаем или повторяем результат захваченной группы 1 и 2 соответственно позже в коде.

согласно порядку кода (?: животное) должна быть группа 1 и (?:=) должна быть группа 2 и продолжается..

, но и давать ?: мы делаем матч-группу не захваченной(которые не отсчитываются в совпадении группа, поэтому номер группировки начинается с первой захваченной группы, а не с не захваченной), так что повторение результата match-group (?: animal) не может быть вызван позже в коде.

надеюсь, это объясняет использование группы без захвата.

Введите описание изображения здесь


Ну, я разработчик JavaScript и попытаюсь объяснить его значение, относящееся к JavaScript.

рассмотрим сценарий, где вы хотите, чтобы соответствовать cat is animal когда вы хотели бы матч кошка и животное, и оба должны иметь is В между ними.

 // this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]

 // using lookahead pattern it will match only "cat" we can
 // use lookahead but the problem is we can not give anything
 // at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]

 //so I gave another grouping parenthesis for animal
 // in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]

 // we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]

в сложных регулярных выражениях может возникнуть ситуация, когда вы хотите использовать большое количество групп, некоторые из которых существуют для сопоставления повторений, а некоторые из них-для обеспечения обратных ссылок. По умолчанию текст, соответствующий каждой группе, загружается в массив backreference. Там, где у нас есть много групп и только нужно иметь возможность ссылаться на некоторые из них из массива backreference, мы можем переопределить это поведение по умолчанию, чтобы сообщить регулярному выражению, что группы существуют только для обработки повторений и не должны быть захвачены и сохранены в массиве backreference.


одна интересная вещь, с которой я столкнулся,-это тот факт, что у вас может быть группа захвата внутри группы без захвата. Посмотрите ниже regex для сопоставления веб-адресов:

var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

строка входного url:

var url = "http://www.ora.com:80/goodparts?q#fragment";

первая группа в моем регулярном выражении (?:([A-Za-z]+):) - Это группа без захвата, которая соответствует схеме протокола и двоеточию : характер, т. е. http: но когда я работал ниже кода, я видел, что 1-й индекс возвращаемого массива содержал строка http когда я думал, что http и оба не будут сообщаться, поскольку они находятся внутри группы без захвата.

console.debug(parse_url_regex.exec(url));

enter image description here

я думал, если первая группа (?:([A-Za-z]+):) является группой без захвата, то почему она возвращается http строка в выходном массиве.

Итак, если вы заметили, что есть вложенная группа ([A-Za-z]+) внутри группы без захвата. Эта вложенная группа ([A-Za-z]+) группа захвата (не имея ?: в начале) сам по себе внутри группы без захвата (?:([A-Za-z]+):). Вот почему текст http все-таки попадает в плен, но символ, который находится внутри группы без захвата, но вне группы захвата, не сообщается в выходном массиве.


Я не могу прокомментировать верхние ответы, чтобы сказать следующее: Я хотел бы добавить явную точку, которая подразумевается только в верхних ответах:

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

для доступа к определенной части регулярного выражения без определенных посторонних символов вам всегда нужно будет использовать .group(<index>)


tl; dr группы без захвата, как следует из названия, являются частями регулярного выражения, которые вы не хотите включать в матч и ?: - Это способ определить группу как незахватывающие.

скажем, у вас есть адрес электронной почты example@example.com. Следующее регулярное выражение создаст два группы, часть id и @example.com часть. (\p{Alpha}*[a-z])(@example.com). Для простоты мы извлекаем все доменное имя, включая @ характер.

теперь предположим, вам нужна только идентификационная часть адреса. То, что вы хотите сделать, это захватить первую группу результата матча, в окружении () в регулярном выражении и способ сделать это-использовать синтаксис группы без захвата, т. е. ?:. Так что регулярное выражение (\p{Alpha}*[a-z])(?:@example.com) вернет только id часть электронной почты.


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

переменные захвата, $1 и т. д. недопустимы, если совпадение не удалось, и они также не очищены.

#!/usr/bin/perl  
use warnings;
use strict;   
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
    print "Fred wants a  ";
}
else
{
    print "Fred dont wants a  ";
}

В приведенном выше примере, чтобы избежать захвата Бронто в $1, (?:) предназначенный. Если шаблон сопоставлен, то $1 захватывается как следующий сгруппированный шаблон. Таким образом, выход будет следующим:

Fred wants a burger

это полезно, если вы не хотите, чтобы спички были сохранены .


Откройте Google Chrome devTools, а затем вкладку консоли: и введите это:

"Peace".match(/(\w)(\w)(\w)/)

запустить его и вы увидите:

["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]

на JavaScript RegExp engine захватывает три группы, элементы с индексами 1,2,3. Теперь используйте метку non-capturing, чтобы увидеть результат.

"Peace".match(/(?:\w)(\w)(\w)/)

результат:

["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]

Это очевидно, что не группа захвата.