Não conheço todos os detalhes do seu caso de uso, o contexto da aplicação, etc. Mas enfim, seguem alguns comentários que espero que ajudem:
Sua regex também pegaria o texto Qual ônibos vai no sentido centro? Preciso comprar tudo isso hoje
(veja). Isso porque ele pega qualquer texto que contenha determinadas palavras, a única exigência é que pelo menos uma de cada grupo apareça.
Na verdade, por causa dos lookaheads (os trechos que começam com (?=
), elas podem até estar fora de ordem. Por exemplo, a frase Tudo faz sentido, diga
também seria capturada pela regex, veja. Isso porque o lookahead só vê se algo existe à frente, mas depois volta para onde estava (no caso, ao início) e procura pelo restante da expressão. Ou seja, ele vê se existe "qual", "diga" ou "explique" em qualquer parte, depois volta e vê se existe "significado", "sentido" ou "resposta" (também em qualquer parte, não necessariamente depois de "qual"/"diga"/"explique"), e depois faz o mesmo para "vida", "universo" ou "tudo".
Para uma regex mais rígida, uma sugestão seria eliminar os lookaheads e também o uso de .*
(pois é isso que permite "qualquer coisa", já que o ponto corrresponde a qualquer caractere, ou seja, até mesmo "frases" como sem sentido, -qual!@#$%-vida.123
dariam match, veja).
Uma sugestão - bem mais rígida - seria:
^(qual|diga|explique) (o s(ignifica|enti)do|a resposta) d(a vida|o universo|e tudo)[?.]?$
Agora a frase só pode começar com "qual", "diga" ou "explique". Como elas estão no início, não precisa mais do \b
, pois o ^
já garante que elas devem estar no começo e não haverá nenhum outro caractere antes. E como depois tem um espaço, então também não precisa do \b
depois, pois agora deve obrigatoriamente ter um espaço.
As palavras "significado" e "sentido" podem ser encurtadas para s(ignifica|enti)do
, e o artigo "o" antes é para garantir que a frase fique correta ("explique o sentido", "diga o significado"). A outra alternativa é "a resposta". Depois temos outro espaço e o trecho final, que cobre as possibilidades "da vida", "do universo" e "de tudo" - como o d
é comum a todos, deixei ele de fora, e depois dele tem as outras alternativas. E novamente, como há um espaço antes e depois, não precisa mais do \b
.
No final, [?.]?
indica que a frase pode ou não terminar com um ponto ou interrogação (pois entendo que "Explique o sentido da vida" não precisa de interrogação). Só pra deixar claro, [?.]
indica que pode ser interrogação ou ponto, e o ?
depois indica que é opcional. Veja aqui a diferença. Claro, se a pontuação não for opcional, use apenas [?.]
.
Eu habilitei a flag case insensitive para a frase poder começar com maiúscula (da mesma forma que vc fez). Só que aí também vai pegar frases como "diGA o SENtidO da VIda". Se a ideia é que somente a primeira letra possa ser maiúscula, melhor tirar a flag e trocar a regex para:
^([qQ]ual|[dD]iga|[eE]xplique) (o s(ignifica|enti)do|a resposta) d(a vida|o universo|e tudo)[?.]?$
Claro que agora ficou bem mais rígido, só vai aceitar frases que se encaixem perfeitamente nesta estrutura. Se quiser deixar mais flexível, vai ter que incluir mais elementos opcionais no meio, não tem jeito. Usar .*
deixa mais simples, mas permite qualquer coisa, inclusive caracteres que não são letras nem pontuação. E deixar mais rígido elimina falsos positivos, mas dificulta a compreensão e futura manutenção. Não tem jeito, sempre é um trade-off, e depende do que vc quer considerar, dos dados que a aplicação recebe, etc.
Outra possível melhoria: por padrão, os parênteses criam grupos de captura, mas se vc não precisa disso (e só quer o match completo com a frase toda), basta trocar por grupos de não-captura, trocando a abertura de parênteses por (?:
.
No fim, é como eu já disse no outro comentário, é fácil fazer uma regex que identifique os casos válidos, mas é difícil fazer uma que também ignore os inválidos.