Como os grupos que não capturam, ou seja (?:)
, são usados em expressões regulares e para que servem?
Como os grupos que não capturam, ou seja (?:)
, são usados em expressões regulares e para que servem?
Respostas:
Deixe-me tentar explicar isso com um exemplo.
Considere o seguinte texto:
http://stackoverflow.com/
/programming/tagged/regex
Agora, se eu aplicar o regex abaixo sobre ele ...
(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
... Eu obteria o seguinte resultado:
Match "http://stackoverflow.com/"
Group 1: "http"
Group 2: "stackoverflow.com"
Group 3: "/"
Match "/programming/tagged/regex"
Group 1: "https"
Group 2: "stackoverflow.com"
Group 3: "/questions/tagged/regex"
Mas não me importo com o protocolo - só quero o host e o caminho da URL. Então, altero a regex para incluir o grupo que não captura (?:)
.
(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
Agora, meu resultado fica assim:
Match "http://stackoverflow.com/"
Group 1: "stackoverflow.com"
Group 2: "/"
Match "/programming/tagged/regex"
Group 1: "stackoverflow.com"
Group 2: "/questions/tagged/regex"
Vejo? O primeiro grupo não foi capturado. O analisador usa-o para corresponder ao texto, mas o ignora mais tarde, no resultado final.
Conforme solicitado, deixe-me tentar explicar os grupos também.
Bem, os grupos servem a muitos propósitos. Eles podem ajudá-lo a extrair informações exatas de uma correspondência maior (que também pode ser nomeada), permitem revidar um grupo correspondente anterior e podem ser usadas para substituições. Vamos tentar alguns exemplos, vamos?
Imagine que você tenha algum tipo de XML ou HTML (saiba que o regex pode não ser a melhor ferramenta para o trabalho , mas é bom como exemplo). Você deseja analisar as tags para poder fazer algo assim (adicionei espaços para facilitar a compreensão):
\<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
\<(.+?)\> [^<]*? \</\1\>
O primeiro regex possui um grupo nomeado (TAG), enquanto o segundo usa um grupo comum. Ambas as expressões regulares fazem a mesma coisa: elas usam o valor do primeiro grupo (o nome da tag) para corresponder à tag de fechamento. A diferença é que o primeiro usa o nome para corresponder ao valor e o segundo usa o índice do grupo (que começa em 1).
Vamos tentar algumas substituições agora. Considere o seguinte texto:
Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.
Agora, vamos usar esse regex idiota sobre ele:
\b(\S)(\S)(\S)(\S*)\b
Essa expressão regular corresponde a palavras com pelo menos três caracteres e usa grupos para separar as três primeiras letras. O resultado é este:
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"
...
Portanto, se aplicarmos a sequência de substituição:
$1_$3$2_$4
... sobre isso, estamos tentando usar o primeiro grupo, adicionar um sublinhado, usar o terceiro grupo, depois o segundo grupo, adicionar outro sublinhado e depois o quarto grupo. A sequência resultante seria como a abaixo.
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.
Você também pode usar grupos nomeados para substituições, usando ${name}
.
Para brincar com regexes, recomendo http://regex101.com/ , que oferece uma boa quantidade de detalhes sobre como o regex funciona; Ele também oferece alguns mecanismos de regex para você escolher.
Você pode usar a captura de grupos para organizar e analisar uma expressão. Um grupo de não captura tem o primeiro benefício, mas não possui a sobrecarga do segundo. Você ainda pode dizer que um grupo de não captura é opcional, por exemplo.
Digamos que você queira corresponder ao texto numérico, mas alguns números podem ser escritos como 1º, 2º, 3º, 4º, ... Se você deseja capturar a parte numérica, mas não o sufixo (opcional), pode usar um grupo que não captura .
([0-9]+)(?:st|nd|rd|th)?
Isso corresponderá a números no formato 1, 2, 3 ... ou no formato 1, 2, 3, ... mas capturará apenas a parte numérica.
?:
é usado quando você deseja agrupar uma expressão, mas não deseja salvá-la como uma parte correspondente / capturada da sequência.
Um exemplo seria algo para corresponder a um endereço IP:
/(?:\d{1,3}\.){3}\d{1,3}/
Observe que eu não me importo em salvar os três primeiros octetos, mas o (?:...)
agrupamento permite que eu reduza a expressão regular sem incorrer na sobrecarga de capturar e armazenar uma correspondência.
Isso torna o grupo sem captura, o que significa que a substring correspondida por esse grupo não será incluída na lista de capturas. Um exemplo em ruby para ilustrar a diferença:
"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
(?:)
não produz uma captura, não demonstrar um exemplo útil de (?:)
. (?:)
é útil quando você deseja agrupar uma subexpressão (por exemplo, quando você deseja aplicar quantificadores a uma subexpressão não atômica ou se deseja restringir o escopo de a |
), mas não deseja capturar nada.
MOTIVAÇÃO HISTÓRICA:
A existência de grupos não capturadores pode ser explicada com o uso de parênteses.
Considere as expressões (a|b)c
e a|bc
, devido à prioridade da concatenação |
, essas expressões representam dois idiomas diferentes ( {ac, bc}
e {a, bc}
respectivamente).
No entanto, os parênteses também são usados como um grupo correspondente (como explicado pelas outras respostas ...).
Quando você deseja colocar parênteses, mas não capturar a subexpressão, use GRUPOS NÃO CAPTURANTES. No exemplo,(?:a|b)c
Deixe-me tentar isso com um exemplo:
Código Regex: (?:animal)(?:=)(\w+)(,)\1\2
Seqüência de pesquisa:
Linha 1 - animal=cat,dog,cat,tiger,dog
Linha 2 - animal=cat,cat,dog,dog,tiger
Linha 3 - animal=dog,dog,cat,cat,tiger
(?:animal)
-> Grupo 1 não capturado
(?:=)
-> Grupo 2 não capturado
(\w+)
-> Grupo capturado 1
(,)
-> Grupo capturado 2
\1
-> resultado do grupo 1 capturado, ou seja, na linha 1 é gato, na linha 2 é gato, na linha 3 é cachorro.
\2
-> resultado do grupo 2 capturado, isto é, vírgula (,)
Portanto, neste código, fornecemos \1
e \2
lembramos ou repetimos o resultado dos grupos capturados 1 e 2, respectivamente, posteriormente no código.
De acordo com a ordem do código (?:animal)
, o grupo 1 (?:=)
deve ser o grupo 2 e continua.
mas, dando ao ?:
não tornar o grupo de correspondência não capturado (que não conta no grupo correspondente, o número de agrupamento começa no primeiro grupo capturado e não o não capturado), para que a repetição do resultado do grupo de correspondência (?:animal)
não pode ser chamado mais tarde no código.
Espero que isso explique o uso de grupos que não capturam.
Grupos que capturam você pode usar posteriormente na regex para corresponder OU você pode usá-los na parte de substituição da regex. Criar um grupo de não captura simplesmente isenta esse grupo de ser usado por um desses motivos.
Grupos que não capturam são ótimos se você estiver tentando capturar muitas coisas diferentes e existem alguns grupos que não deseja capturar.
Essa é a razão pela qual eles existem. Enquanto você aprende sobre grupos, aprende sobre grupos atômicos , eles fazem muito! Também existem grupos de pesquisa, mas eles são um pouco mais complexos e pouco utilizados.
Exemplo de uso posterior no regex (referência anterior):
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>
[Encontra uma tag xml (sem suporte ao ns)]
([A-Z][A-Z0-9]*)
é um grupo de captura (nesse caso, é o nome da tag)
Posteriormente na regex, \1
significa que ele corresponderá apenas ao mesmo texto que estava no primeiro grupo (o ([A-Z][A-Z0-9]*)
grupo) (nesse caso, ele corresponderá à tag final).
Bem, eu sou um desenvolvedor de JavaScript e tentarei explicar seu significado referente ao JavaScript.
Considere um cenário em que você deseja combinar cat is animal
quando gostaria de combinar gato e animal e ambos devem ter um is
entre eles.
// 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"]
Em expressões regulares complexas, pode surgir a situação em que você deseja usar um grande número de grupos, alguns dos quais existem para correspondência de repetições e outros para fornecer referências anteriores. Por padrão, o texto correspondente a cada grupo é carregado na matriz de referência anterior. Onde temos muitos grupos e precisamos apenas fazer referência a alguns deles da matriz de referência anterior, podemos substituir esse comportamento padrão para dizer à expressão regular que certos grupos estão lá apenas para manipulação de repetição e não precisam ser capturados e armazenados na matriz de referência anterior.
Não posso comentar nas respostas principais para dizer o seguinte: gostaria de adicionar um ponto explícito, que está implícito apenas nas respostas principais:
O grupo (?...)
que não captura não remove nenhum caractere da correspondência completa original, apenas reorganiza a regex visualmente para o programador.
Para acessar uma parte específica da regex sem caracteres estranhos definidos, você sempre precisará usar .group(<index>)
tl; dr grupos que não capturam, como o nome sugere, são as partes da regex que você não deseja incluir na correspondência e ?:
é uma maneira de definir um grupo como não capturando.
Digamos que você tenha um endereço de e-mail example@example.com
. O regex a seguir criará dois grupos , a parte id e a parte @ example.com. (\p{Alpha}*[a-z])(@example.com)
. Por uma questão de simplicidade, estamos extraindo todo o nome de domínio, incluindo o @
personagem.
Agora, digamos, você só precisa da parte id do endereço. O que você quer fazer é pegar o primeiro grupo do resultado da partida, cercado pela ()
regex e a maneira de fazer isso é usar a sintaxe do grupo que não captura ?:
. Portanto, a regex (\p{Alpha}*[a-z])(?:@example.com)
retornará apenas a parte de identificação do email.
Uma coisa interessante que me deparei é o fato de que você pode ter um grupo de captura dentro de um grupo não-captura. Confira abaixo a regex para correspondência de URLs da Web:
var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
String de URL de entrada:
var url = "http://www.ora.com:80/goodparts?q#fragment";
O primeiro grupo no meu regex (?:([A-Za-z]+):)
é um grupo não capturável que corresponde ao esquema de protocolo e ao :
caractere de dois pontos , ou seja, http:
mas quando eu estava executando abaixo do código, estava vendo que o primeiro índice da matriz retornada continha a string http
quando eu pensava nisso http
e dois pontos :
ambos não serão relatados, pois estão dentro de um grupo que não captura.
console.debug(parse_url_regex.exec(url));
Eu pensei que se o primeiro grupo (?:([A-Za-z]+):)
é um grupo não-captura, então por que está retornandohttp
seqüência na matriz de saída.
Portanto, se você perceber que há um grupo aninhado ([A-Za-z]+)
dentro do grupo que não captura. Esse grupo aninhado ([A-Za-z]+)
é um grupo de captura (que não existe ?:
no início) em si mesmo dentro de um grupo de não captura (?:([A-Za-z]+):)
. É por isso que o texto http
ainda é capturado, mas o :
caractere de dois pontos que está dentro do grupo de não captura, mas fora do grupo de captura, não é relatado na matriz de saída.
Abra seu devTools do Google Chrome e, em seguida, guia Console: e digite isto:
"Peace".match(/(\w)(\w)(\w)/)
Execute-o e você verá:
["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]
O JavaScript
mecanismo RegExp captura três grupos, os itens com índices 1,2,3. Agora use a marca de não captura para ver o resultado.
"Peace".match(/(?:\w)(\w)(\w)/)
O resultado é:
["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]
É óbvio o que não é um grupo de captura.
Eu acho que daria a resposta. Não use variáveis de captura sem verificar se a correspondência foi bem-sucedida.
As variáveis de captura $1
, etc, não são válidas, a menos que a correspondência tenha sido bem-sucedida e também não são limpas.
#!/usr/bin/perl
use warnings;
use strict;
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
print "Fred wants a $1";
}
else
{
print "Fred dont wants a $1 $2";
}
No exemplo acima, para evitar capturar bronto in $1
, (?:)
é usado.
Se o padrão for correspondido, $1
será capturado como o próximo padrão agrupado.
Portanto, a saída será a seguinte:
Fred wants a burger
É útil se você não quiser que as correspondências sejam salvas.
É extremamente simples, podemos entender com um exemplo simples de data, suponha que a data seja mencionada como 1º de janeiro de 2019 ou 2 de maio de 2019 ou qualquer outra data e queremos simplesmente convertê-la em dd / mm / aaaa , não precisaremos do mês nome que é janeiro ou fevereiro para esse assunto, portanto, para capturar a parte numérica, mas não o sufixo (opcional), você pode usar um grupo que não captura.
então a expressão regular seria,
([0-9]+)(?:January|February)?
É simples assim.