Expressões regulares: Existe um operador AND?


708

Obviamente, você pode usar o |(pipe?) Para representar OR, mas existe uma maneira de representar ANDtambém?

Especificamente, eu gostaria de combinar parágrafos de texto que contenham TODAS uma determinada frase, mas em nenhuma ordem específica.


1
Você quer dizer que deseja encontrar frases em um texto, onde cada uma dessas frases é uma permutação válida das palavras em uma determinada frase?
Nietzche-jou

2
Estou colocando isso aqui porque três ou quatro respostas o ignoram. Lookahead não corresponde ao mesmo comprimento para cada cláusula, a menos que termine em $. (? = A *) Um lookahead poderia coincidir com quatro personagens, e outro 6. Por exemplo, (? = AAB) irá corresponder aabaaaaba
Zachary Vance

2
tente usar apenas o caractere "espaço" para o operador "AND".

1 I'd like to match paragraphs of text. 2. Contendo texto fora de ordem . O número 1 está aberto à interpretação. O número 2 pode ser feito de duas maneiras. Caminho 1:, (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}Caminho 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)onde, neste caso, a correspondência do parágrafo neste caso é indefinida até que a definição do parágrafo seja formalizada.

Respostas:


385

Use uma expressão regular não consumidora.

A notação típica (ou seja, Perl / Java) é:

(?=expr)

Isso significa "corresponder expr, mas depois continuar a correspondência no ponto de partida original".

Você pode fazer quantas delas quiser, e isso será um "e". Exemplo:

(?=match this expression)(?=match this too)(?=oh, and this)

Você pode até adicionar grupos de captura às expressões que não consomem, se precisar salvar alguns dados.


3
perl -e "q {algumas coisas e coisas} = ~ / (? = algumas) (? = coisas) (? = coisas) /? print 'yes': print 'no'" imprime 'no'.
Robert P

27
Deve-se mencionar que este exemplo em particular é chamado de asserção positiva à vista. Tem outros usos além de "e". Observe que o texto não é consumido.
Strager 22/01/09

7
Usar (? =) Como esse resulta em uma regex que nunca pode ter sucesso. Mas é a conjunção analógica para |. O OP está errado no que ele acha que resolverá seu problema.
Nietzche-jou

10
perl -e "q {algumas coisas e coisas} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print 'yes': print 'no'"
kriss

3
Você pode adicionar um exemplo fácil no código perl na sua resposta?
Pithikos

343

Você precisa usar o lookahead, como alguns dos outros respondentes disseram, mas o lookahead deve contabilizar outros caracteres entre a palavra-alvo e a posição atual de correspondência. Por exemplo:

(?=.*word1)(?=.*word2)(?=.*word3)

O .*primeiro lookahead permite combinar o número de caracteres necessário antes de chegar a "word1". Em seguida, a posição da partida é redefinida e o segundo lookahead procura "word2". Redefina novamente e a parte final corresponde a "palavra3"; como é a última palavra que você está procurando, não é necessário que ele fique de cabeça para baixo, mas não dói.

Para corresponder a um parágrafo inteiro, você precisa ancorar a regex nas duas extremidades e adicionar uma final .*para consumir os caracteres restantes. Usando a notação no estilo Perl, isso seria:

/^(?=.*word1)(?=.*word2)(?=.*word3).*$/m

O modificador 'm' é para o modo multilinha; permite que o ^e $corresponda aos limites do parágrafo ("limites da linha" na expressão regular). Nesse caso, é essencial que você não use o modificador 's', que permite que o metacaractere de ponto corresponda às novas linhas, bem como a todos os outros caracteres.

Finalmente, você quer ter certeza de que está combinando palavras inteiras e não apenas fragmentos de palavras mais longas; portanto, é necessário adicionar limites de palavras:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m

8
Exatamente certo - também há um tutorial sobre isso! ocpsoft.org/tutorials/regular-expressions/and-in-regex
Lincoln

9
. Muito obrigado * este fazer a diferença
Gennadiy Ryabkin

1
+1 para uma resposta clara e sucinta, mostrando um dos melhores usos para lookaheads (diferente dos usos como um hack para contar a porcentagem correspondente de uma senha). :)
ZX81

1
@Liam :. O MySQL usa o sabor POSIX ERE, então não. Ele efetivamente sacrifica recursos em favor do desempenho, o que me parece razoável. Há mais informações aqui .
Alan Moore

3
substituir .*com [\s\S]*em javascript se você tem novas linhas como .no motor de regex de javascript não coincide com novas linhas e não pode ser feita para com modificadores
Wesley Smith

41

Veja este exemplo:

Temos dois regexps A e B e queremos combinar os dois, portanto, no pseudo-código, fica assim:

pattern = "/A AND B/"

Pode ser escrito sem usar o operador AND assim:

pattern = "/NOT (NOT A OR NOT B)/"

no PCRE:

"/(^(^A|^B))/"

regexp_match(pattern,data)

24
Isso é verdade em termos de lógica formal, mas não ajuda em nada aqui. Nas regexes, NÃO pode ser ainda mais difícil de expressar do que AND.
Alan Moore

@ marvin_dpr Funcionou para mim no CMake enquanto a outra sugestão (?=expr)não. Parece ser dependente da implementação.
Melebius

38
Não ^significa "início de string" na sintaxe regex?
Lambda Fairy

3
Em regex em geral, ^é negação apenas no início de uma classe de personagem. A menos que o CMake esteja fazendo algo realmente desagradável (a ponto de chamar sua linguagem de correspondência de padrões "regex" pode ser considerada enganosa ou incorreta), acho que o fato de que funcionou para você foi um acidente isolado.
Tripleee

29

Você pode fazer isso com uma expressão regular, mas provavelmente desejará algo mais. Por exemplo, use vários regexp e combine-os em uma cláusula if.

Você pode enumerar todas as permutações possíveis com um regexp padrão, como este (corresponde a, bec em qualquer ordem):

(abc)|(bca)|(acb)|(bac)|(cab)|(cba)

No entanto, isso gera uma regexp muito longa e provavelmente ineficiente, se você tiver mais do que alguns termos.

Se você estiver usando alguma versão estendida do regexp, como Perl ou Java, eles terão melhores maneiras de fazer isso. Outras respostas sugeriram o uso de uma operação com lookahead positivo.


10
Não acho que sua abordagem seja mais ineficiente do que três lookaheads com seu retorno catastrófico. Certifique-se de que é mais longo para escrever, mas observe que você pode facilmente gerar o padrão automaticamente. Observe que você pode melhorá-lo para falhar mais rapidamente a(bc|cb)|b(ac|ca)|c(ab|ba). E o mais importante, você pode usá-lo com todo o sabor regex.
Casimir et Hippolyte

27

O operador AND está implícito na sintaxe RegExp.
O operador OR deve ser especificado com um tubo.
O seguinte RegExp:

var re = /ab/;

significa a letra a E a letra b.
Também trabalha com grupos:

var re = /(co)(de)/;

significa o grupo co E o grupo de.
Substituir AND (implícito) por um OR exigiria as seguintes linhas:

var re = /a|b/;
var re = /(co)|(de)/;

29
Infelizmente, não é isso que o OP solicitou. Isso encontra qualquer coisa nessa ordem, enquanto eles os desejavam em qualquer ordem. Confira a resposta em stackoverflow.com/users/20938/alan-moore abaixo da qual é a correta.
JESii

1
@ JESii obrigado pelo seu ponto, você está certo e eu entendi mal a pergunta de Hugoware, concentrei-me particularmente em sua primeira frase. A resposta certa é o uso adequado do operador lookahead, como AlanMoore escreveu. De qualquer maneira, acho que alguém pode achar útil meu esclarecimento, como já foi votado, para que eu não jogue tudo fora. Saudações.
Emanuele Del Grande

13

No seu caso, não é possível executar AND em vários resultados correspondentes? no pseudocódigo

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...

3
Estou em uma situação em que tenho algum código que é uma tabela de regras de dados, com uma única sequência de correspondência de padrão de regex para testar a validade da regra. Mover para vários testes não é algo que eu possa fazer no meu caso, e geralmente também nos casos de outras pessoas!
Alan Wolfe

11

Por que não usar awk?
com awk regex AND, OR importa é tão simples

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile

9

Se você usar expressões regulares do Perl, poderá usar lookahead positivo:

Por exemplo

(?=[1-9][0-9]{2})[0-9]*[05]\b

seriam números maiores que 100 e divisíveis por 5


8

Você pode canalizar sua saída para outro regex. Usando grep, você pode fazer o seguinte:

grep A | grep B


8

Além da resposta aceita

Vou lhe dar alguns exemplos práticos que deixarão as coisas mais claras para alguns de vocês. Por exemplo, digamos que temos essas três linhas de texto:

[12/Oct/2015:00:37:29 +0200] // only this + will get selected
[12/Oct/2015:00:37:x9 +0200]
[12/Oct/2015:00:37:29 +020x]

Veja a demonstração aqui DEMO

O que queremos fazer aqui é selecionar o sinal +, mas apenas se for depois de dois números com um espaço e se for antes de quatro números. Essas são as únicas restrições. Usaríamos essa expressão regular para alcançá-la:

'~(?<=\d{2} )\+(?=\d{4})~g'

Observe que se você separar a expressão, ela fornecerá resultados diferentes.

Ou talvez você queira selecionar algum texto entre as tags ... mas não as tags! Então você pode usar:

'~(?<=<p>).*?(?=<\/p>)~g'

para este texto:

<p>Hello !</p> <p>I wont select tags! Only text with in</p> 

Veja a demonstração aqui DEMO


Qual resposta foi aceita? Por favor, adicione um link para ele no futuro.
James Brown

6

A ordem está sempre implícita na estrutura da expressão regular. Para realizar o que você deseja, você precisará corresponder a sequência de entrada várias vezes com diferentes expressões.

O que você deseja fazer não é possível com uma única regexp.


Não é tecnicamente impossível, mas não vale a pena implementar. Eu não sei por que alguém votou mal ...
P Robert P

13
Provavelmente porque não é apenas possível, é simples, supondo que o seu sabor regex seja compatível com lookaheads. E essa é uma boa aposta; a maioria das principais linguagens de programação atuais os suporta.
Alan Moore

3

Use AND fora da expressão regular. No PHP, o operador lookahead não parecia funcionar para mim; em vez disso, usei este

if( preg_match("/^.{3,}$/",$pass1) && !preg_match("/\s{1}/",$pass1))
    return true;
else
    return false;

O regex acima corresponderá se o tamanho da senha for de 3 caracteres ou mais e não houver espaços na senha.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.