Verifique se uma linha tem pelo menos 2 letras diferentes

1

Eu tenho um arquivo de texto que tem uma palavra em todas as linhas. Estou tentando remover as linhas que não contêm pelo menos duas letras diferentes. Por exemplo, o arquivo se parece com algo assim:

words
books
aaa
letters 
zzzz

E eu quero que o arquivo de saída seja assim:

words
books
letters

Eu tentei dividir cada palavra em letras separadas do que agrupá-las usando uniq -c e, em seguida, wc -l , mas fiquei preso na instrução if. Também acredito que deve haver uma maneira mais fácil de fazê-lo, eu simplesmente não conseguia pensar em qualquer outra maneira de abordar esse problema.

    
por Cloud 03.11.2017 / 11:05

3 respostas

6

Supondo que você queira dizer caractere em vez de letra (como em, você também deseja excluir linhas que contenham ... ou 11 , embora . ou 1 não sejam letras):

grep -vx -e '' -e '\(.\)*'

ou:

grep -vx '\(\(.\)*\)\{0,1\}'

Isso é remover ( -v ) linhas ou linhas vazias que começam com um caractere ( . ) seguido pelo mesmo caractere ( sendo uma referência anterior ao que foi capturado por \(...\) ) repetido 0 ou mais vezes ( * ) até o final da linha ( -x ancora o padrão no início e no final da linha).

Portáteis, você não pode usar egrep ou grep -E aqui, pois os EREs padrão não têm referências anteriores (somente os BREs).

Para linhas que contêm pelo menos duas letras diferentes, ignorando os outros tipos de caracteres (usaremos [[:alpha:]] aqui para letra , ou seja, qualquer caractere considerado alfabético na sua localidade):

grep -vx '[^[:alpha:]]*
[^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\)*[^[:alpha:]]*'

(em duas linhas, que é outra maneira de passar dois padrões diferentes). Ou:

grep -vx '[^[:alpha:]]*\([^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\)*[^[:alpha:]]*\)\{0,1\}'

Essa pessoa removeria linhas como 12345aaa (apenas uma letra) ou -+-+-+- (nenhuma letra).

Se você quiser excluir Aaaa linhas também (caso ignorar quando comparar letras), adicione a opção -i .

Note que ele funciona no nível do personagem, então ele pode não fazer o que você espera se houver grafemas expressos com mais de um caractere. Por exemplo, ele excluiria uma linha como essa por:

 $ printf 'e\u0300e\u0301\n'
 èé

(assumindo o GNU printf ou compatível), mas não um como:

 $ printf '\ue8\ue9\n'
 èé

(onde e\u300 é a forma decomposta e \ue8 da forma pré-composta da è grapheme; e (U + 0065) e è (U + 00E8) são alfabéticos , mas não o U + 0300 ou U + 0301 combinando acentos graves / agudos).

Para trabalhar com grafemas, você pode usar pcregrep ou GNU grep com a opção -P :

Para o primeiro caso (pelo menos dois clusters de grafema diferentes):

grep -vxP '(?:(\X)*)?'

Para o segundo caso (pelo menos dois clusters de grafemas letter ):

grep -vxP '(?:(?=\PL)\X)*(?:((?=\pL)\X)(?:(?:(?=\PL)\X)*(?!\pM))*(?:(?=\PL)\X)*)?'

Em que (?=\PL)\X é um cluster de grafema sem letras (um cluster de grafema ( \X ) forneceu (?=...) , inicia com um cluster de letra não grafada ( \PL ) e (?=\pL)\X a letra grafite.

\pL corresponde ao Carta unicode corretamente. Ao contrário da classe de caracteres [:alpha:] POSIX, também inclui letras de scripts não alfabéticos.

Note que ele consideraria e\u300\u301 , e\u301\u300 , \ue9\u300 , \ue8\u301 como quatro grupos diferentes, embora todos fossem e com sotaque agudo e grave.

Tenha também cuidado com caracteres como (U + FB03) que contêm várias letras em um caractere .

Com o PCRE, você também pode usar abordagens positivas :

  • pelo menos 2 caracteres diferentes:

    grep -P '(.).*(?!).'
    
  • pelo menos 2 caracteres de letras diferentes:

    grep -P '(\pL).*(?!)\pL'
    
  • pelo menos 2 clusters de grafema diferentes:

    grep -P '^\X*(\X)\X*(?!(?!\pM))\X'
    

    aquele não funcionaria adequadamente com o Corão Hangul em forma decomposta (pelo menos). PCRE (ao contrário do RE de perl com \b{g} ) não tem operador de limite de grafema (AFAIK) e tem suporte limitado para propriedades unicode. Estamos usando (?!\pM) (o que significa nesse contexto: "desde que não seja seguido por um caractere de combinação de caracteres") como uma aproximação, mas isso não funciona para letras Hangul de várias partes / sílaba caracteres em que as partes não têm essa propriedade. Ele excluiria 려련련 , por exemplo. Agora também podemos argumentar que cada parte é uma letra ...

    Com perl 5,22 ou acima, você pode escrevê-lo:

    perl -Mopen=locale -lne 'print if /\b{g}(\X).*\b{g}(?!\b{g})\X/'
    
  • pelo menos 2 diferentes conjuntos de grafos letter :

    grep -P '^\X*((?=\pL)\X)\X*(?!(?!\pM))(?=\pL)\X'
    

    Novamente, não funciona em 려련련 . Com perl :

    perl -Mopen=locale -lne 'print if /\b{g}(?=\pL)(\X).*\b{g}(?!\b{g})(?=\pL)\X/'
    

Com perl , podemos usar abordagens mais simples, como:

  • pelo menos 2 caracteres diferentes:

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(/./g) >= 2'
    
  • pelo menos 2 caracteres de letras diferentes:

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(/\pL/g) >= 2'
    
  • pelo menos 2 clusters de grafema diferentes:

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(/\X/g) >= 2'
    
  • pelo menos 2 diferentes conjuntos de grafos letter :

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(grep /^\pL/, /\X/g) >= 2'
    
por 03.11.2017 / 11:22
1

De acordo com sua saída esperada, você quer pular palavras que tenham mais de 2 caracteres idênticos :

abordagem

grep :

grep -vE '(.)(){2,}' file

O utput:

words
books
letters 

Para modificar o arquivo inplace , você pode aplicar a seguinte sed abordagem:

sed -Ei '/(.)(){2}/d' file
    
por 03.11.2017 / 11:21
0

Pesquisa positiva da consulta:

while read -r line; do 
    n=$(echo "$line" | egrep -o . | sort -u);
    [[ ${#n} -gt 1 ]] && echo "$line"; 
done < file
    
por 03.11.2017 / 11:26