Eu tenho uma expressão regular que captura um padrão UMA apenas se a string contiver um padrão B em algum lugar antes UMA.
Digamos, por uma questão de simplicidade, que UMA é bd{3}b
(ou seja, três dígitos) e B é a palavra "foo".
Portanto, o Regex que tenho é (?<=b(?:foo)b.*?)(?<A>bd{3}b)
.
(?<= # look-behind
b(?:foo)b # pattern B
.*? # variable length
)
(?<A>bd{3}b) # pattern A
Por exemplo, para a corda
"foo text 111, 222 and not bar something 333 but foo 444 and better 555"
captura
(111, 222, 333, 444, 555)
Recebi um novo requisito e agora tenho que excluir as capturas que são precedidas por padrão C, vamos dizer que C é a palavra "bar". O que eu quero construir é um regex que expressa
(?<= # look-behind
b(?:foo)b # pattern B
??????????? # anything that does not contains pattern C
)
(?<A>bd{3}b) # pattern A
Então, na string de exemplo, terei que capturar
(111, 222, 444, 555)
Claro algo como (?<=b(?:foo)b.*?)(?<!b(?:bar)b.*?)(?<A>bd{3}b)
(?<= # look-behind
b(?:foo)b # pattern B
.*?
)
(?<! # negative look-behind
b(?:bar)b # pattern C
.*?
)
(?<A>bd{3}b) # pattern A
não funcionará, pois excluirá tudo após a primeira aparição de "barra" e a captura será
(111, 222)
O regex (?<=b(?:foo)b(?!.*?(?:bbarb)).*?)(?<A>bd{3}b)
(?<= # look-behind
b(?:foo)b # pattern B
(?! # negative lookahead
.*? # variable lenght
(?:bbarb) # pattern C
)
.*? # variable lenght
)
(?<A>bd{3}b) # pattern A
também não funcionará porque para o primeiro "foo" na minha string de teste, ele sempre encontrará a "barra" como um sufixo e só irá capturar
(444, 55)
Até agora, usando Correspondência condicional de expressões e (agora) sabendo que enquanto dentro de um lookbehind, .net corresponde e captura da direita para a esquerda, Consegui criar o seguinte regex (?<=(?(C)(?!)| (?:bfoob))(?:(?<!bbar)s|(?<C>bbars)|[^s])*)(?<A>bd{3}b)
(?<= # look-behind
(?(C) # if capture group C is not empty
(?!) # fail (pattern C was found)
| # else
(?:bfoob) # pattern B
)
(?:
(?<!bbar)s # space not preceeded by pattern C (consume the space)
|
(?<C>bbars) # pattern C followed by space (capture in capture group C)
|
[^s] # anything but space (just consume)
)* # repeat as needed
)
(?<A>bd{3}b) # pattern A
que funciona, mas é muito complexo como os padrões UMA, B e C são muito mais complexos do que os exemplos que postei aqui.
É possível simplificar este regex? Talvez usando grupos de equilíbrio?
Respostas:
2 para resposta № 1Uma opção simples é muito semelhante ao segundo padrão de Casimir et Hippolyte:
foo(?>(?<A>bd{3}b)|(?!bar).)+
- Começar com
foo
(?>
…|(?!bar).)+
- Pare de combinar se você viubar
.(?<A>bd{3}b)
e capture todos os A "s que você vir ao longo do caminho.- Grupo atômico
(?>)
não é necessário neste caso, o retrocesso não iria bagunçar tudo de qualquer maneira.
Da mesma forma, ele pode ser convertido em um lookbehind:
(?<=foo(?:(?!bar).)*?)(?<A>bd{3}b)
Isso tem a vantagem de combinar apenas os números. O lookbehind afirma que há um foo
antes de A, mas não há um bar
.
Exemplo de trabalho
Ambos assumem que B e C são um tanto simples.
3 para resposta № 2
Você pode usar um padrão baseado no G
âncora que corresponde à posição após a partida anterior:
(?:G(?!A)|bfoob)(?:(?!b(?:bar|d{3})b).)*(d{3})
detalhes:
(?:
G(?!A) # contiguous to a previous match and not at the start of the string
| # OR
bfoob # foo: the condition for the first match
)
(?:(?!b(?:bar|d{3})b).)* # all that is not "bar" or a 3 digit number (*)
(d{3})
(*) Observe que, se você puder usar um subpadrão melhor (ou seja,que não testa cada caractere com um lookahead contendo uma alternância) para sua situação real, não hesite em mudá-la. (por exemplo, algo baseado em classes de personagens: [^bd]*(?>(?:B[bd]+|b(?!arb)|d(?!ddb))[^bd]*)*
)
Outra maneira: como o mecanismo de regex .net é capaz de armazenar capturas repetidas, você também pode escrever isso:
bfoob(?:(?:(?!b(?:bar|d{3})b).)*(d{3}))+
Mas desta vez, você precisa fazer um loop sobre cada ocorrência de foo para extrair os resultados no grupo 1. É menos prático, mas o padrão é mais rápido, pois não começa com uma alternância.
Note que se "bar"
e "d{3}"
começa e termina com caracteres de palavra, você pode escrever o padrão de uma forma mais eficiente:
bfoo(?:W+(?>(?!barb)w+W+)*?(d{3}))+b
Outro jeito:divida sua string em "foo" e "bar" (preserve o delimitador), faça um loop sobre cada parte. Quando a parte for "foo" defina um sinalizador como verdadeiro, quando a parte for "bar" defina-o como falso e quando não for "t" foo "ou" bar "extraia os números se o sinalizador for verdadeiro.
2 para resposta № 3
Já que você perguntou, é possível com grupos de equilíbrio, mas provavelmente não é necessário.
A # Match from the start of the string
(?> # Atomic group. no backsies.
(?<B>(?<-B>)?foo) # If we see "foo", push it to stack B.
# (?<-B>)? ensures B only has one item - if there are two,
# one is popped.
|(?<-B>bar) # When we see a bar, reset the foo.
|(?(B)(?<A>bd{3}b)|(?!)) # If foo is set, we are allowed to capture A.
|. # Else, just advance by one character.
)+
z # Match until the end of the string.
Se quisermos ser mais inteligentes (o que provavelmente não queremos), podemos combinar a maioria dos ramos no condicional:
A
(?>
(?(B)
(?:(?<A>bd{3}b)|(?<-B>bar))
| # else
(?<B>foo)
)
|.
)+
z
Mais uma vez, é possível, mas balancear grupos não é a melhor opção aqui, principalmente porque não estamos balanceando nada, apenas verificando se um sinalizador está definido ou não.