Como obter linhas onde uma palavra específica é repetida exatamente N vezes?

8

Para esta entrada fornecida:

How to get This line that this word repeated 3 times in THIS line?
But not this line which is THIS word repeated 2 times.
And I will get This line with this here and This one
A test line with four this and This another THIS and last this

Eu quero esta saída:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Obter linhas inteiras contém apenas três palavras "isto" repetidas. (correspondência sem distinção entre maiúsculas e minúsculas)

αғsнιη
fonte
4
Para o eleitor amplo demais: como uma pergunta pode ser mais específica?
Jacob Vlijm
@JacobVlijm Na medida em que existem "muitas respostas possíveis". Escolha $RANDOM_LANGUAGE- alguém será capaz de encontrar uma solução.
muru
@muru Eu diria o contrário, limitá-lo a um idioma tornaria uma questão centrada na programação (linguagem). Agora é uma questão centrada no problema . Talvez haja muitas soluções possíveis (idiomas), mas não tantas soluções óbvias.
Jacob Vlijm

Respostas:

13

Em perl, substitua this-o sem distinção entre maiúsculas e minúsculas e conte o número de substituições:

$ perl -ne 's/(this)/$1/ig == 3 && print' <<EOF
How to get This line that this word repeated 3 times in THIS line?
But not this line which is THIS word repeated 2 times.
And I will get This line with this here and This one
A test line with four this and This another THIS and last this
EOF
How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Usando uma contagem de correspondências :

perl -ne 'my $c = () = /this/ig; $c == 3 && print'

Se você possui o GNU awk, é uma maneira muito simples:

gawk -F'this' -v IGNORECASE=1 'NF == 4'

O número de campos será um a mais que o número de separadores.

muru
fonte
Por que substituir? não podemos contar diretamente sem substituir?
αғsнιη
Na verdade, podemos contar, o código é um pouco mais longo: stackoverflow.com/questions/9538542/…
muru
Voto positivo para o comando gawk.
Sri
9

Supondo que seu arquivo de origem seja tmp.txt,

grep -iv '.*this.*this.*this.*this' tmp.txt | grep -i '.*this.*this.*this.*'

O grep esquerdo gera todas as linhas que não possuem 4 ou mais ocorrências que não diferenciam maiúsculas de minúsculas de "this" em tmp.txt.

O resultado é canalizado para o grep direito, que gera todas as linhas com 3 ou mais ocorrências no resultado grep esquerdo.

Atualização: Graças ao @Muru, aqui está a melhor versão desta solução,

grep -Eiv '(.*this){4,}' tmp.txt | grep -Ei '(.*this){3}'

substitua 4 por n + 1 e 3 por n.

Sri
fonte
Isso falharia para N> 4. E a primeira grepprecisa terminar *.
Ps95
1
Quero dizer, você não pode escrever isso para N = 50. E a pergunta é exatamente para três, então você precisa de outro grep que descarte todas as saídas que contenham menos que ou igual a dois this. grep -iv '.*this.*this.*this.*this.*' tmp.txt | grep -i '.*this.*this.*this.* |grep -iv '.*this.*this.'
Ps95
@ prakharsingh95 Não falhou para n> 4 e * não é necessário no primeiro grep.
Sri
1
@KasiyA Qual é a sua opinião sobre a minha resposta?
Sri
5
Simplifique um pouco: grep -Eiv '(.*this){4,}' | grep -Ei '(.*this){3}'- isso pode torná-lo prático para N = 50.
muru
9

Em python, isso faria o trabalho:

#!/usr/bin/env python3

s = """How to get This line that this word repeated 3 times in THIS line?
But not this line which is THIS word repeated 2 times.
And I will get This line with this here and This one
A test line with four this and This another THIS and last this"""

for line in s.splitlines():
    if line.lower().count("this") == 3:
        print(line)

saídas:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Ou para ler de um arquivo, com o arquivo como argumento:

#!/usr/bin/env python3
import sys

file = sys.argv[1]

with open(file) as src:
    lines = [line.strip() for line in src.readlines()]

for line in lines:
    if line.lower().count("this") == 3:
        print(line)
  • Cole o script em um arquivo vazio, salve-o como find_3.py, execute-o pelo comando:

    python3 /path/to/find_3.py <file_withlines>
    

É claro que a palavra "this" pode ser substituída por qualquer outra palavra (ou outra seção de string ou linha), e o número de ocorrências por linha pode ser definido como qualquer outro valor na linha:

    if line.lower().count("this") == 3:

Editar

Se o arquivo fosse grande (centenas de milhares / milhões de linhas), o código abaixo seria mais rápido; ele lê o arquivo por linha em vez de carregá-lo de uma vez:

#!/usr/bin/env python3
import sys
file = sys.argv[1]

with open(file) as src:
    for line in src:
        if line.lower().count("this") == 3:
            print(line.strip())
Jacob Vlijm
fonte
Eu não sou especialista em python, como posso ler um arquivo? obrigado
α Jansнιη
1
@KasiyA editado para usar o arquivo como argumento.
Jacob Vlijm
Apenas curioso: por que você não usou um gerador no segundo trecho de código?
muru
6

Você pode brincar um pouco com awkisso:

awk -F"this" 'BEGIN{IGNORECASE=1} NF==4' file

Isso retorna:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Explicação

  • O que fazemos é definir o separador de campos para thissi mesmo. Dessa forma, a linha terá tantos campos +1 quanto a palavra thisaparecer.

  • Para torná-lo sem distinção entre maiúsculas e minúsculas, usamos IGNORECASE = 1. Ver referência: caso sensibilidade em Matching .

  • Então, é apenas uma questão de dizer NF==4para obter todas essas linhas thisexatamente três vezes. Não é necessário mais código, pois {print $0}(ou seja, imprimir a linha atual) é o comportamento padrão de awkquando uma expressão é avaliada True.

fedorqui
fonte
Já postou , mas boa explicação.
Muru
@ muru oh, eu não vi isso! Minhas desculpas e +1 para você.
Fedorqui
5

Assumindo que as linhas são armazenadas em um arquivo chamado FILE:

while read line; do 
    if [ $(grep -oi "this" <<< "$line" | wc -w)  = 3 ]; then 
        echo "$line"; 
    fi  
done  <FILE
ps95
fonte
1
Obrigado, você pode remover seu sed ...comando e adicionar a -oopção grep -oi ....
αғsнιη
Mais simples:$(grep -ic "this" <<<"$line")
muru
2
@muru Não, a -copção contará o número de linhas que correspondem a "this" e não o número de "this" words em cada linha.
αғsнιη
1
@KasiyA Ah, sim. Foi mal.
muru
@ KasiyA, não seria -le -wseria equivalente neste caso?
ps95
4

Se você estiver no Vim:

g/./if len(split(getline('.'), 'this\c', 1)) == 4 | print | endif

Isso imprimirá apenas linhas correspondentes.

Bohr
fonte
Bom exemplo para procurar linhas com n ocorrências de palavras, ao usar o Vim.
Sri
0

Solução one-liner Ruby:

$ ruby -ne 'print $_ if $_.chomp.downcase.scan(/this/).count == 3' < input.txt                                    
How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Funciona de uma maneira bastante simples: redirecionamos o arquivo para o stdin do ruby, o ruby ​​obtém a linha do stdin, limpa-o com chompe downcase, e scan().countnos fornece o número de ocorrências de uma substring.

Sergiy Kolodyazhnyy
fonte