Jak uzyskać wiersze, w których określone słowo jest powtarzane dokładnie N razy?

8

Dla tego podanego wejścia:

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

Chcę tego wyniku:

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

Uzyskiwanie całych wierszy zawiera tylko trzy powtórzone „to” słowa. (dopasowanie bez rozróżniania wielkości liter)

αғsнιη
źródło
4
Zbyt szerokiemu wyborcy: w jaki sposób pytanie może być bardziej szczegółowe?
Jacob Vlijm,
@JacobVlijm W tym, że istnieje „zbyt wiele możliwych odpowiedzi”. Wybierz $RANDOM_LANGUAGE- ktoś będzie w stanie znaleźć rozwiązanie.
muru
@muru Powiedziałbym wręcz przeciwnie, ograniczenie go do jednego języka sprawiłoby, że byłoby to pytanie skoncentrowane na programowaniu (języku). Teraz jest to pytanie skoncentrowane na problemie . Być może istnieje wiele możliwych rozwiązań (języków), ale nie tak wiele oczywistych.
Jacob Vlijm

Odpowiedzi:

13

W perlzamień na thissiebie bez rozróżniania wielkości liter i policz liczbę zamienników:

$ 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

Korzystanie licznik meczów zamiast:

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

Jeśli masz GNU awk, bardzo prosty sposób:

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

Liczba pól będzie o jedną więcej niż liczba separatorów.

muru
źródło
Dlaczego warto wymienić? nie możemy tego policzyć bezpośrednio bez wymiany?
αғsнιη
Rzeczywiście możemy liczyć, kod jest nieco dłuższy: stackoverflow.com/questions/9538542/…
muru
Głosuj za poleceniem gawk.
Sri
9

Zakładając, że plik źródłowy to tmp.txt,

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

Lewy grep wyprowadza wszystkie wiersze, które nie zawierają 4 lub więcej wystąpień „this” bez rozróżniania wielkości liter w tmp.txt.

Wynik jest przesyłany do prawego grep, który wyprowadza wszystkie linie z 3 lub więcej wystąpieniami w wyniku lewego grep.

Aktualizacja: Dzięki @Muru jest lepsza wersja tego rozwiązania,

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

zamień 4 na n + 1 i 3 na n.

Sri
źródło
To nie powiedzie się dla N> 4. I pierwsza grepmusi się zakończyć *.
ps95
1
Mam na myśli, że nie możesz napisać tego dla N = 50. Pytanie dotyczy dokładnie trzech, więc potrzebujesz kolejnego grepa, który odrzuca wszystkie dane wyjściowe zawierające mniej niż dwa this. grep -iv '.*this.*this.*this.*this.*' tmp.txt | grep -i '.*this.*this.*this.* |grep -iv '.*this.*this.'
ps95
@ prakharsingh95 To nie zawiodło dla n> 4 i * nie jest wymagane przy pierwszym grep.
Sri
1
@KasiyA jakie jest twoje zdanie na temat mojej odpowiedzi?
Sri
5
Uprość to trochę: grep -Eiv '(.*this){4,}' | grep -Ei '(.*this){3}'- może to być praktyczne dla N = 50.
muru
9

W Pythonie wykona to zadanie:

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

wyjścia:

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

Lub wczytać z pliku z plikiem jako argumentem:

#!/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)
  • Wklej skrypt do pustego pliku, zapisz go jako find_3.py, uruchom go poleceniem:

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

Oczywiście słowo „to” można zastąpić dowolnym innym słowem (lub innym ciągiem lub sekcją wiersza), a liczbę wystąpień w wierszu można ustawić na dowolną inną wartość w wierszu:

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

Edytować

Jeśli plik byłby duży (setki tysięcy / miliony linii), poniższy kod byłby szybszy; czyta plik w wierszu zamiast ładować plik naraz:

#!/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
źródło
Nie jestem ekspertem od Pythona, jak mogę czytać z pliku? dzięki
αғsнιη
1
@ KasiyA edytowano, aby użyć pliku jako argumentu.
Jacob Vlijm
Ciekawe: dlaczego nie użyłeś generatora w drugim fragmencie kodu?
muru
6

Możesz się z awktym pograć :

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

Zwraca to:

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

Wyjaśnienie

  • To, co robimy, to zdefiniowanie separatora pól dla thissiebie. W ten sposób linia będzie miała tyle pól +1, ile razy thispojawi się słowo .

  • Aby uniknąć rozróżniania wielkości liter, używamy IGNORECASE = 1. Patrz odniesienie: Rozróżnianie wielkości liter w dopasowywaniu .

  • Zatem wystarczy powiedzieć, NF==4że wszystkie te wiersze mają thisdokładnie trzy razy. Nie jest już potrzebny żaden kod, ponieważ {print $0}(to znaczy wydrukuje bieżący wiersz) jest domyślnym zachowaniem, awkgdy wyrażenie ocenia True.

fedorqui
źródło
Już opublikowane , ale dobre wyjaśnienie.
muru
@muru och, nie widziałem tego! Przepraszam i +1 za ciebie.
fedorqui
5

Zakładając, że wiersze są przechowywane w pliku o nazwie FILE:

while read line; do 
    if [ $(grep -oi "this" <<< "$line" | wc -w)  = 3 ]; then 
        echo "$line"; 
    fi  
done  <FILE
ps95
źródło
1
Dziękujemy, możesz usunąć swoje sed ...polecenie i zamiast tego dodać -oopcję grep -oi ....
αғsнιη
Prostsze:$(grep -ic "this" <<<"$line")
muru
2
@muru Nie, -copcja zlicza liczbę wierszy pasujących do „tego”, a nie liczby „tego” słowa w każdej linii.
αғsнιη
1
@KasiyA Ah, tak. Mój błąd.
muru
@KasiyA, czy w takim przypadku nie byłoby -li -wbędzie równoważne?
ps95
4

Jeśli jesteś w Vimie:

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

To po prostu wydrukuje dopasowane linie.

Bohr
źródło
Przyjemny przykład do wyszukiwania linii z n wystąpieniami słowa podczas używania Vima.
Sri
0

Rubinowe rozwiązanie jednowarstwowe:

$ 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

Działa w dość prosty sposób: przekierowujemy plik do standardowego ruby, ruby ​​pobiera linię ze standardowego, oczyszcza go za pomocą chompi downcase, i scan().countdaje nam liczbę wystąpień podłańcucha.

Sergiy Kolodyazhnyy
źródło