¿Cómo puedo obtener líneas donde una palabra específica se repite exactamente N veces?

8

Para esta entrada dada:

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

Quiero esta salida:

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

Obtener líneas enteras contiene solo tres palabras repetidas "esto". (coincidencia entre mayúsculas y minúsculas)

αғsнιη
fuente
44
Para el votante demasiado amplio: ¿cómo puede una pregunta posiblemente volverse más específica?
Jacob Vlijm el
@JacobVlijm En eso hay "demasiadas respuestas posibles". Elija $RANDOM_LANGUAGE: alguien podrá encontrar una solución.
Muru
@muru Diría lo contrario, limitarlo a un idioma lo convertiría en una pregunta centrada en la programación (lenguaje). Ahora se trata de un problema en cuestión centrada. Quizás haya muchas soluciones posibles (idiomas), pero no tantas obvias.
Jacob Vlijm

Respuestas:

13

En perl, reemplace thiscon mayúsculas y minúsculas y cuente el número de reemplazos:

$ 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 un recuento de coincidencias en su lugar:

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

Si tienes GNU awk, una forma muy simple:

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

El número de campos será uno más que el número de separadores.

muru
fuente
¿Por qué reemplazar? no podemos contarlo directamente sin reemplazar?
αғsнιη
De hecho, podemos contar, el código es un poco más largo: stackoverflow.com/questions/9538542/…
muru
Votación a favor del comando gawk.
Sri
9

Asumiendo que su archivo fuente es tmp.txt,

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

El grep izquierdo genera todas las líneas que no tienen 4 o más ocurrencias de "this" que distinguen entre mayúsculas y minúsculas en tmp.txt.

El resultado se canaliza al grep derecho, que genera todas las líneas con 3 o más ocurrencias en el resultado grep izquierdo.

Actualización: Gracias a @Muru, aquí está la mejor versión de esta solución,

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

reemplace 4 con n + 1 y 3 con n.

Sri
fuente
Esto fallaría para N> 4. Y la primera grepnecesita terminar *.
ps95
1
Quiero decir que no puedes escribir esto para N = 50. Y la pregunta es exactamente para tres, por lo que necesitas otro grep que descarte todas las salidas que contengan menos o igual que dos this. grep -iv '.*this.*this.*this.*this.*' tmp.txt | grep -i '.*this.*this.*this.* |grep -iv '.*this.*this.'
ps95
@ prakharsingh95 No falló para n> 4 y * no se requiere en el primer grep.
Sri
1
@KasiyA ¿Cuál es tu opinión sobre mi respuesta?
Sri
55
Simplifíquelo un poco: grep -Eiv '(.*this){4,}' | grep -Ei '(.*this){3}'esto podría hacerlo práctico para N = 50.
muru
9

En python, esto haría el trabajo:

#!/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)

salidas:

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

O para leer desde un archivo, con el archivo 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)
  • Pegue el script en un archivo vacío, guárdelo como find_3.py, ejecútelo con el comando:

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

Por supuesto, la palabra "this" se puede reemplazar por cualquier otra palabra (u otra cadena o sección de línea), y el número de ocurrencias por línea se puede establecer en cualquier otro valor en la línea:

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

Editar

Si el archivo fuera grande (cientos de miles / millones de líneas), el siguiente código sería más rápido; lee el archivo por línea en lugar de cargar el archivo a la 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
fuente
No soy un experto en python, ¿cómo puedo leer el archivo? gracias
αғsнιη
1
@KasiyA editado para usar el archivo como argumento.
Jacob Vlijm
Solo curiosidad: ¿por qué no usaste un generador en el segundo fragmento de código?
Muru
6

Puedes jugar un poco con awkesto:

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

Esto devuelve:

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

Explicación

  • Lo que hacemos es definir el separador de campo para thissí mismo. De esta forma, la línea tendrá tantos campos +1 como veces thisaparezca la palabra .

  • Para hacerlo insensible a mayúsculas y minúsculas, utilizamos IGNORECASE = 1. Ver referencia: Sensibilidad a mayúsculas y minúsculas en el emparejamiento .

  • Entonces, es solo una cuestión de decir NF==4que todas esas líneas tengan thisexactamente tres veces. No se necesita más código, ya {print $0}que (es decir, imprimir la línea actual) es el comportamiento predeterminado de awkcuando una expresión se evalúa True.

fedorqui
fuente
Ya publicado , pero buena explicación.
Muru
@muru ¡oh, no lo vi! Mis disculpas y +1 por ti.
fedorqui
5

Suponiendo que las líneas se almacenan en un archivo llamado FILE:

while read line; do 
    if [ $(grep -oi "this" <<< "$line" | wc -w)  = 3 ]; then 
        echo "$line"; 
    fi  
done  <FILE
ps95
fuente
1
Gracias, puede eliminar su sed ...comando y agregar la -oopción para en su grep -oi ...lugar.
αғsнιη
Más simple:$(grep -ic "this" <<<"$line")
muru
2
@muru No, la -copción contará el número de líneas que coinciden con "este", no el número de "este" palabras en cada línea.
αғsнιη
1
@KasiyA Ah, sí. Culpa mía.
muru
@KasiyA, ¿no sería -ly -wsería equivalente en este caso?
ps95
4

Si estás en Vim:

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

Esto solo imprimirá líneas coincidentes.

Bohr
fuente
Un buen ejemplo para buscar líneas con n apariciones de palabras, cuando se usa Vim.
Sri
0

Solución Ruby de una sola línea:

$ 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 una manera bastante simple: redirigimos el archivo al stdin de ruby, ruby ​​obtiene la línea del stdin, lo limpia con chompy downcase, y scan().countnos da varias ocurrencias de una subcadena.

Sergiy Kolodyazhnyy
fuente