Comment obtenir des lignes où un mot spécifique est répété exactement N fois?

8

Pour cette entrée donnée:

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

Je veux cette sortie:

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

L'obtention de lignes entières ne contient que trois mots "this" répétés. (correspondance insensible à la casse)

αғsнιη
la source
4
Pour l'électeur trop large: comment une question peut-elle devenir plus précise?
Jacob Vlijm
@JacobVlijm En ce qu'il y a "trop ​​de réponses possibles". Choisissez $RANDOM_LANGUAGE- quelqu'un pourra y trouver une solution.
muru
@muru Je dirais le contraire, le limiter à une seule langue en ferait une question de programmation (langue) centrée. Maintenant, c'est une question centrée sur le problème. Il existe peut-être de nombreuses solutions (langues) possibles, mais pas tellement évidentes.
Jacob Vlijm

Réponses:

13

Dans perl, remplacez thispar lui-même sans tenir compte de la casse et comptez le nombre de remplacements:

$ 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

Utilisez plutôt un nombre de correspondances :

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

Si vous avez GNU awk, une manière très simple:

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

Le nombre de champs sera un de plus que le nombre de séparateurs.

muru
la source
Pourquoi remplacer? on ne peut pas le compter directement sans le remplacer?
αғsнιη
En effet on peut compter, le code est un peu plus long: stackoverflow.com/questions/9538542/…
muru
Votez pour la commande gawk.
Sri
9

En supposant que votre fichier source est tmp.txt,

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

Le grep gauche affiche toutes les lignes qui n'ont pas 4 occurrences ou plus insensibles à la casse de "this" dans tmp.txt.

Le résultat est dirigé vers le grep droit, qui génère toutes les lignes avec 3 occurrences ou plus dans le résultat grep gauche.

Mise à jour: grâce à @Muru, voici la meilleure version de cette solution,

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

remplacer 4 par n + 1 et 3 par n.

Sri
la source
Cela échouerait pour N> 4. Et le premier grepdoit se terminer *.
ps95
1
Je veux dire que vous ne pouvez pas écrire ceci pour N = 50. Et la question est pour exactement trois donc vous avez besoin d'un autre grep qui rejette toutes les sorties contenant moins que ou égal à deux this. grep -iv '.*this.*this.*this.*this.*' tmp.txt | grep -i '.*this.*this.*this.* |grep -iv '.*this.*this.'
ps95
@ prakharsingh95 Il n'a pas échoué pour n> 4 et * n'est pas requis dans le premier grep.
Sri
1
@KasiyA quelle est votre opinion sur ma réponse?
Sri
5
Simplifiez-le un peu: grep -Eiv '(.*this){4,}' | grep -Ei '(.*this){3}'- cela pourrait le rendre pratique pour N = 50.
muru
9

En python, cela ferait l'affaire:

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

les sorties:

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 pour lire à partir d'un fichier, avec le fichier comme argument:

#!/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)
  • Collez le script dans un fichier vide, enregistrez-le sous find_3.py, exécutez-le avec la commande:

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

Bien sûr, le mot "ceci" peut être remplacé par n'importe quel autre mot (ou toute autre chaîne ou section de ligne), et le nombre d'occurrences par ligne peut être défini sur n'importe quelle autre valeur de la ligne:

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

Éditer

Si le fichier était volumineux (centaines de milliers / millions de lignes), le code ci-dessous serait plus rapide; il lit le fichier par ligne au lieu de charger le fichier à la fois:

#!/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
la source
Je ne suis pas un expert en python, comment lire à partir d'un fichier? merci
αғsнιη
1
@KasiyA a été modifié pour utiliser le fichier comme argument.
Jacob Vlijm
Juste curieux: pourquoi n'avez-vous pas utilisé un générateur dans le deuxième extrait de code?
muru
6

Vous pouvez jouer un peu avec awkpour ça:

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

Cela renvoie:

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

Explication

  • Ce que nous faisons est de définir le séparateur de champ pour thislui-même. De cette façon, la ligne aura autant de champs +1 que de fois où le mot thisapparaît.

  • Pour le rendre insensible à la casse, nous utilisons IGNORECASE = 1. Voir référence: Sensibilité à la casse dans l'appariement .

  • Ensuite, il suffit de dire NF==4que toutes ces lignes ont thisexactement trois fois. Aucun code supplémentaire n'est nécessaire, car {print $0}(c'est-à-dire, imprimer la ligne actuelle) est le comportement par défaut awklorsqu'une expression est évaluée True.

fedorqui
la source
Déjà posté , mais bonne explication.
muru
@muru oh, je ne l'ai pas vu! Mes excuses et +1 pour vous.
fedorqui
5

En supposant que les lignes sont stockées dans un fichier nommé FILE:

while read line; do 
    if [ $(grep -oi "this" <<< "$line" | wc -w)  = 3 ]; then 
        echo "$line"; 
    fi  
done  <FILE
ps95
la source
1
Merci, vous pouvez supprimer votre sed ...commande et ajouter une -ooption pour à la grep -oi ...place.
αғsнιη
Plus simple:$(grep -ic "this" <<<"$line")
muru
2
@muru Non, l' -coption comptera le nombre de lignes correspondant à "ce" et non le nombre de "ce" mots dans chaque ligne.
αғsнιη
1
@KasiyA Ah, oui. Ma faute.
muru
@KasiyA, ne serait-ce pas -let -wserait-il équivalent dans ce cas?
ps95
4

Si vous êtes à Vim:

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

Cela imprimera simplement les lignes correspondantes.

Bohr
la source
Bel exemple pour rechercher des lignes avec n occurrences de mot, lors de l'utilisation de Vim.
Sri
0

Solution Ruby one-liner:

$ 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

Fonctionne d'une manière assez simple: nous redirigeons le fichier vers stdin de ruby, ruby ​​obtient la ligne de stdin, le nettoie avec chompet downcase, et scan().countnous donne le nombre d'occurrences d'une sous-chaîne.

Sergiy Kolodyazhnyy
la source