grep para encontrar palavras com todas as vogais [duplicado]

2

Usando grep -E e expressões regulares, preciso encontrar palavras que contenham um grupo de caracteres em nenhuma ordem específica. Por exemplo, uma palavra contendo todas as vogais (aeiou), mas as vogais podem aparecer na palavra em qualquer ordem.

    
por shackman11 18.09.2014 / 01:19

3 respostas

7

Esta regra "contém todos os aeiou em alguma ordem" não pode ser expressa em uma expressão regular estendida POSIX ( grep -E ) de tamanho razoável.

Aqui está um grande que lista todas as 120 permutações de aeiou como alternativas:

a.*e.*i.*o.*u|a.*e.*i.*u.*o|a.*e.*o.*i.*u|a.*e.*o.*u.*i|a.*e.*u.*i.*o|a.*e.*u.*o.*i|a.*i.*e.*o.*u|a.*i.*e.*u.*o|a.*i.*o.*e.*u|a.*i.*o.*u.*e|a.*i.*u.*e.*o|a.*i.*u.*o.*e|a.*o.*e.*i.*u|a.*o.*e.*u.*i|a.*o.*i.*e.*u|a.*o.*i.*u.*e|a.*o.*u.*e.*i|a.*o.*u.*i.*e|a.*u.*e.*i.*o|a.*u.*e.*o.*i|a.*u.*i.*e.*o|a.*u.*i.*o.*e|a.*u.*o.*e.*i|a.*u.*o.*i.*e|e.*a.*i.*o.*u|e.*a.*i.*u.*o|e.*a.*o.*i.*u|e.*a.*o.*u.*i|e.*a.*u.*i.*o|e.*a.*u.*o.*i|e.*i.*a.*o.*u|e.*i.*a.*u.*o|e.*i.*o.*a.*u|e.*i.*o.*u.*a|e.*i.*u.*a.*o|e.*i.*u.*o.*a|e.*o.*a.*i.*u|e.*o.*a.*u.*i|e.*o.*i.*a.*u|e.*o.*i.*u.*a|e.*o.*u.*a.*i|e.*o.*u.*i.*a|e.*u.*a.*i.*o|e.*u.*a.*o.*i|e.*u.*i.*a.*o|e.*u.*i.*o.*a|e.*u.*o.*a.*i|e.*u.*o.*i.*a|i.*a.*e.*o.*u|i.*a.*e.*u.*o|i.*a.*o.*e.*u|i.*a.*o.*u.*e|i.*a.*u.*e.*o|i.*a.*u.*o.*e|i.*e.*a.*o.*u|i.*e.*a.*u.*o|i.*e.*o.*a.*u|i.*e.*o.*u.*a|i.*e.*u.*a.*o|i.*e.*u.*o.*a|i.*o.*a.*e.*u|i.*o.*a.*u.*e|i.*o.*e.*a.*u|i.*o.*e.*u.*a|i.*o.*u.*a.*e|i.*o.*u.*e.*a|i.*u.*a.*e.*o|i.*u.*a.*o.*e|i.*u.*e.*a.*o|i.*u.*e.*o.*a|i.*u.*o.*a.*e|i.*u.*o.*e.*a|o.*a.*e.*i.*u|o.*a.*e.*u.*i|o.*a.*i.*e.*u|o.*a.*i.*u.*e|o.*a.*u.*e.*i|o.*a.*u.*i.*e|o.*e.*a.*i.*u|o.*e.*a.*u.*i|o.*e.*i.*a.*u|o.*e.*i.*u.*a|o.*e.*u.*a.*i|o.*e.*u.*i.*a|o.*i.*a.*e.*u|o.*i.*a.*u.*e|o.*i.*e.*a.*u|o.*i.*e.*u.*a|o.*i.*u.*a.*e|o.*i.*u.*e.*a|o.*u.*a.*e.*i|o.*u.*a.*i.*e|o.*u.*e.*a.*i|o.*u.*e.*i.*a|o.*u.*i.*a.*e|o.*u.*i.*e.*a|u.*a.*e.*i.*o|u.*a.*e.*o.*i|u.*a.*i.*e.*o|u.*a.*i.*o.*e|u.*a.*o.*e.*i|u.*a.*o.*i.*e|u.*e.*a.*i.*o|u.*e.*a.*o.*i|u.*e.*i.*a.*o|u.*e.*i.*o.*a|u.*e.*o.*a.*i|u.*e.*o.*i.*a|u.*i.*a.*e.*o|u.*i.*a.*o.*e|u.*i.*e.*a.*o|u.*i.*e.*o.*a|u.*i.*o.*a.*e|u.*i.*o.*e.*a|u.*o.*a.*e.*i|u.*o.*a.*i.*e|u.*o.*e.*a.*i|u.*o.*e.*i.*a|u.*o.*i.*a.*e|u.*o.*i.*e.*a

e aqui está uma que é mais curta, mas mais difícil de seguir por causa do aninhamento:

a.*(e.*(i.*[ou]|o.*[iu]|u.*[io])|i.*(e.*[ou]|o.*[eu]|u.*[eo])|o.*(e.*[iu]|i.*[eu]|u.*[ei])|u.*(e.*[io]|i.*[eo]|o.*[ei]))|e.*(a.*(i.*[ou]|o.*[iu]|u.*[io])|i.*(a.*[ou]|o.*[au]|u.*[ao])|o.*(a.*[iu]|i.*[au]|u.*[ai])|u.*(a.*[io]|i.*[ao]|o.*[ai]))|i.*(a.*(e.*[ou]|o.*[eu]|u.*[eo])|e.*(a.*[ou]|o.*[au]|u.*[ao])|o.*(a.*[eu]|e.*[au]|u.*[ae])|u.*(a.*[eo]|e.*[ao]|o.*[ae]))|o.*(a.*(e.*[iu]|i.*[eu]|u.*[ei])|e.*(a.*[iu]|i.*[au]|u.*[ai])|i.*(a.*[eu]|e.*[au]|u.*[ae])|u.*(a.*[ei]|e.*[ai]|i.*[ae]))|u.*(a.*(e.*[io]|i.*[eo]|o.*[ei])|e.*(a.*[io]|i.*[ao]|o.*[ai])|i.*(a.*[eo]|e.*[ao]|o.*[ae])|o.*(a.*[ei]|e.*[ai]|i.*[ae]))

Ambos estão assumindo que as palavras que você está procurando estão em um arquivo com uma palavra por linha. Caso contrário, você terá que alterar todos os . para [^ ] para corresponder aos caracteres não espaciais. (Nota: \S do perl está disponível em muitos mecanismos regexp, possivelmente incluindo o grep que você usa, mas expressões regulares compatíveis com perl não são um recurso padrão do grep, então [^ ] deve ser.)

Eu não tentaria escrever qualquer uma dessas regexps manualmente. Eu usei um gerador de permutação para o primeiro, e fiz um monte de gravação de macro e replaying no vim para escrever o segundo.

Mas vamos ver o que acontece se invertermos o problema. Em vez de tentar corresponder uma string contendo todas as vogais, tente corresponder ao contrário: uma string que esteja ausente de pelo menos uma das vogais .

(De agora em diante, fico com a suposição de uma palavra por linha na entrada).

Uma palavra que não possui o a corresponde a ^[^a]*$ (do começo ao fim, é feita de caracteres diferentes de a ).

Uma palavra que não possui o e corresponde a ^[^e]*$
Uma palavra que está faltando o i corresponde a ^[^i]*$
Uma palavra que está faltando o o corresponde a ^[^o]*$
Uma palavra que não possui o u corresponde a ^[^u]*$

Uma palavra que está faltando em a OU faltando e OU faltando i OU faltando o OU faltando u correspondências

^([^a]*|[^e]*|[^i]*|[^o]*|[^u]*)$

Portanto, este comando grep fornece todas as palavras que você não deseja:

grep -E '^([^a]*|[^e]*|[^i]*|[^o]*|[^u]*)$' wordlist

e a prática -v oferece as palavras que você faz deseja:

grep -vE '^([^a]*|[^e]*|[^i]*|[^o]*|[^u]*)$' wordlist

Adicione -i , se desejado.

A técnica de inversão é algo para se ter em mente ao escrever expressões regulares. Às vezes, um regexp realmente complicado é o inverso de um mais simples.

    
por 18.09.2014 / 03:07
1

Precisa ser um comando único grep ? Para encontrar todas as palavras contendo cada vogal pelo menos uma vez (em qualquer ordem), o comando mais simples é

grep a word_list | grep e | grep i | grep o | grep u

Adicione -i se você precisar não diferenciar maiúsculas de minúsculas.

    
por 18.09.2014 / 01:28
0

Isso é um pouco difícil, já que grep não possui um verdadeiro operador AND. Você pode usar vários truques dentro de grep para obter parcialmente um E, mas ele só funciona em certas situações.

Por exemplo, você pode usar o operador de caractere curinga para corresponder sequências onde eles têm seu padrão, mas ele só corresponderá a ele se estiver na mesma ordem exata do seu padrão.

Arquivo de amostra

$ cat words.txt 
aeiou
a1e2i3o4u5
dog
blh
spkz
uoiea

1. Exemplo usando. *

$ grep -E ".*a.*e.*i.*o.*u.*" words.txt 
aeiou
a1e2i3o4u5

Isso corresponde apenas às cadeias que possuem a sequência de aeiou nessa mesma progressão. Mas não corresponde a uoiea .

2. Exemplo usando [aeiou]

$ grep -E [aeiou] words.txt 
aeiou
a1e2i3o4u5
dog
uoiea

Essa abordagem combina, mas é muito solta, combinando com qualquer coisa que contenha pelo menos uma das letras em nosso padrão.

3. Exemplo usando vários greps

$ grep a words.txt | grep e | grep i | grep o | grep u
aeiou
a1e2i3o4u5
uoiea

Se você for forçado a usar grep , essa é realmente a única opção disponível se sua intenção for corresponder a todos os caracteres de um conjunto.

4. Exemplo usando o awk

$ awk '/a/ && /e/ && /i/ && /o/ && /u/ { print }' words.txt 
aeiou
a1e2i3o4u5
uoiea

Como se fosse o caso quando se lida com o Unix. Se você estiver disposto a mudar para a ferramenta apropriada, uma tarefa aparentemente impossível se torna fácil. Aqui, mudando para awk , podemos agora usar um operador AND real ( && ) e agora podemos especificar o conjunto de caracteres que queremos encontrar.

5. Exemplo usando outros switches de grep , (-w)

Se todas as strings que você está combinando forem garantidas como únicas palavras, você poderá usar a opção -w .

   -w, --word-regexp
        Select only those lines containing matches that form whole words. 
        The test is that the matching substring must  either  be  at  the
        beginning  of  the  line,  or  preceded by a non-word constituent 
        character.  Similarly, it must be either at the end of the line or
        followed by a non-word constituent character.  Word-constituent 
        characters are letters, digits, and the underscore.

Portanto, contanto que a série de letras do conjunto [aeiou] seja toda contígua, você pode aproveitar essa opção da seguinte forma:

$ grep -Ew "*[aeiou]*" words.txt 
aeiou
uoiea

Mas se você tiver que lidar com frases com várias letras, também falhará:

$ echo "I love Fort Lauderdale" | grep -Ew "*[aeiou]*" 
$

No entanto, se usarmos a opção -i para que grep não seja sensível a maiúsculas, ela funcionará mais uma vez:

$ echo "I love Fort Lauderdale" | grep -Ewi "*[aeiou]*"
I love Fort Lauderdale
    
por 18.09.2014 / 03:05