Jak policzyć liczbę wystąpień słowa w pliku tekstowym za pomocą wiersza polecenia?


43

Mam duży plik JSON, który znajduje się w jednym wierszu i chcę użyć wiersza polecenia, aby móc policzyć liczbę wystąpień słowa w pliku. Jak mogę to zrobić?


Nie jest jasne, czy słowo powinno być dopasowane zarówno do kluczy, jak i wartości danych JSON, tj. Czy { "key": "the key" }powinno liczyć ciąg keyraz czy dwa razy.
Kusalananda

Odpowiedzi:


45
$ tr ' ' '\n' < FILE | grep WORD | wc -l

Gdzie trzamienia spacje na nowe linie, grepfiltruje wszystkie wynikowe linie pasujące do WORD i wczlicza pozostałe.

Można nawet zapisać wcczęść, używając -copcji grep:

$ tr ' ' '\n' < FILE | grep -c WORD

-cOpcja jest zdefiniowany przez POSIX.

Jeśli nie ma gwarancji, że między słowami są spacje, musisz użyć innego znaku (jako separatora) do zastąpienia. Na przykład trsą części alternatywne

tr '"' '\n'

lub

tr "'" '\n'

jeśli chcesz zastąpić podwójne lub pojedyncze cudzysłowy. Oczywiście możesz także użyć trdo zamiany wielu znaków jednocześnie (pomyśl o różnych rodzajach białych znaków i interpunkcji).

Jeśli musisz policzyć WORD, ale nie przedrostek WORD, WORDsuffix lub prefixWORDsuffix, możesz zawrzeć wzorzec WORD w znacznikach początku / końca linii:

grep -c '^WORD$'

W naszym kontekście, który jest równoważny znacznikom początku / końca słowa:

grep -c '\<WORD\>'

co jeśli nie ma spacji, tzn. nazwa pola jest otoczona cudzysłowami? np. „pole”
mit

@mythz: Następnie zamieniasz cudzysłowy na nowe znaki na tr. Zaktualizuję odpowiedź.
maxschlepzig

1
Ta odpowiedź jest niepoprawna na wiele sposobów. To jest niejasne: powinieneś wyjaśnić, jak wymyślić trpolecenie, które wykona zadanie, zamiast sugerować przykłady, które nigdy nie będą działać we wszystkich sytuacjach. Dopasuje również słowa zawierające szukane słowo. grep -o '\<WORD\>' | wc -lRozwiązanie jest o wiele lepszy.
sam hocevar,

1
@Sam, pytanie pozostawia to trochę otwarte, jeśli wyszukiwane słowo powinno być wyszukiwane jak „WORD” lub „\ <WORD \>” - możesz je przeczytać na dwa sposoby. Nawet jeśli przeczytasz to na 2-ty sposób i tylko na 2-ty sposób, to moja odpowiedź byłaby niepoprawna tylko na 1 sposób. ;) A rozwiązanie „grep -o” jest lepsze tylko wtedy, gdy obsługuje opcję -o - która nie jest określona przez POSIX ... Cóż, nie sądzę, aby użycie tr było tak egzotyczne, aby to nazwać niejasne ...
maxschlepzig

1
@Kusalananda, cóż, to wciąż zdarzenie. Ale jeśli nie chcesz liczyć takich dopasowań podciągów, przeczytaj ostatni akapit mojej odpowiedzi i mój poprzedni komentarz tutaj.
maxschlepzig

24

W przypadku GNU grep działa to: grep -o '\<WORD\>' | wc -l

-o drukuje każdą dopasowaną część każdej linii na osobnej linii.

\<potwierdza początek słowa i \>zapewnia koniec słowa (podobny do Perla \b), więc zapewnia to, że nie dopasujesz łańcucha w środku słowa.

Na przykład,

$ python -c 'import this' | grep '\ <one \>'
Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego.
Przestrzenie nazw to jeden świetny pomysł - zróbmy ich więcej!
$ python -c 'import this' | grep -o '\ <one \>'
 one 
one 
one 
$ python -c 'import this' | grep -o '\ <one \>' | wc -l
3)

1
Lub po prostugrep -wo WORD | wc -l
Stéphane Chazelas

10

To niestety nie działa z GNU coreutils.

grep -o -c WORD file

Jeśli działa na twojej platformie, jest to eleganckie i dość intuicyjne rozwiązanie; ale ludzie GNU wciąż myślą.


2
Mój zły, błąd jest nadal otwarty: savannah.gnu.org/bugs/?33080
tripleee

1
Szkoda, że ​​byłby to najbardziej elegancki
MasterScrat

To zadziałało dla mnie!
ThisaruG

To jest źle. Zlicza to liczbę linii ze wzorem WORD. PO chce całkowitej liczby wystąpień.
Pierre B,

@PierreB Dlatego mówię, że GNU grepma tutaj błąd. Nie jest jasne, z POSIX co semantyka łączy -ci -opowinno być więc ta obecnie nie jest przenośny. Dziękuję za komentarz; Zaktualizowałem tę odpowiedź.
tripleee

7
sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl 

To polecenie sprawia, że:

  1. Zastąp wszystkie znaki niealfanumeryczne spacją.
  2. Wszystkie podziały linii są również konwertowane na spacje.
  3. Zmniejsza wszystkie wiele spacji do jednej spacji
  4. Wszystkie spacje są teraz konwertowane na podziały linii. Każde słowo w linii.
  5. Tłumaczy wszystkie słowa na małe litery, aby uniknąć „Witaj” i „cześć”, aby były innymi słowami
  6. Sortuje tekst
  7. Liczy i usuwa równe linie
  8. Sortuje odwrotnie, aby policzyć najczęstsze słowa
  9. Dodaj numer wiersza do każdego słowa, aby poznać pozycję wyrazu w całości

Na przykład, jeśli chcę przeanalizować pierwszą wiadomość Linusa Torvalda:

Od: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds) Grupy dyskusyjne: comp.os.minix Temat: Co chciałbyś zobaczyć w minix? Podsumowanie: mała ankieta dla mojego nowego systemu operacyjnego Message-ID: <1991Aug25.205708.9541@klaava.Helsinki.FI> Data: 25 sierpnia 91 20:57:08 GMT Organizacja: University of Helsinki

Witajcie wszyscy za pomocą minix -

Robię (darmowy) system operacyjny (po prostu hobby, nie będzie duży i profesjonalny jak GNU) dla 386 (486) AT klonów. Trwa to od kwietnia i zaczyna się przygotowywać. Chciałbym uzyskać informację zwrotną na temat rzeczy, które ludzie lubią / nie lubią w minix, ponieważ mój system operacyjny nieco to przypomina (ten sam fizyczny układ systemu plików (z przyczyn praktycznych) między innymi).

Obecnie przenosiłem bash (1.08) i gcc (1.40) i wydaje się, że wszystko działa. Oznacza to, że w ciągu kilku miesięcy dostanę coś praktycznego i chciałbym wiedzieć, jakie funkcje chciałaby większość ludzi. Wszelkie sugestie są mile widziane, ale nie obiecuję, że je zrealizuję 🙂

Linus (torvalds@kruuna.helsinki.fi)

PS. Tak - nie zawiera żadnego kodu minix i ma wielowątkowy fs. NIE jest to protable (używa przełączania zadań 386 itp.) I prawdopodobnie nigdy nie będzie obsługiwać niczego poza dyskami twardymi AT, ponieważ to wszystko, co mam :-(

Tworzę plik o nazwie linus.txt , wklejam zawartość, a następnie piszę w konsoli:

sed -e 's/[^[:alpha:]]/ /g' linus.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl 

Wyjście byłoby:

 1        7 i
 2        5 to
 3        5 like
 4        5 it
 5        5 and
 6        4 minix
 7        4 a
 8        3 torvalds
 9        3 of
10        3 helsinki
11        3 fi
12        3 any
13        2 would
14        2 won
15        2 what
16        ...

Jeśli chcesz wizualizować tylko pierwsze 20 słów:

sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | head -n 20

Ważne jest, aby pamiętać, że polecenie tr „AZ” „A-Z” nie suport UTF-8 jeszcze tak, że w językach obcych apres słowne byłyby tłumaczone jako Apres.

Jeśli chcesz wyszukać tylko jedno słowo, możesz dodać grep na końcu:

sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | grep "\sword_to_search_for$"

W skrypcie o nazwie search_freq :

#!/bin/bash
sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | grep "\s$1$"

Skrypt musi zostać wywołany:

 search_freq word_to_search_for

sed: -e expression #2, char 7: unterminated „polecenie”, to także liczy wszystkie słowa, prawda? Ale OP poprosił tylko o jeden. Przydałoby się również trochę wyjaśnienia.
phk

Przepraszam, miałem błąd. Zmieniłem polecenie i skomentowałem odpowiedź. Moim zdaniem, z pytania nie można wiedzieć, czy chciałby otrzymać walutę tylko jednego słowa lub częstotliwość występowania. Ale jeśli chcesz dostać tylko jedno słowo, możesz dodać grep na końcu.
Roger Borrell,

3

W zależności od tego, czy chcesz dopasować słowo w kluczach, czy w wartościach danych JSON, prawdopodobnie będziesz chciał wyodrębnić tylko klucze lub tylko wartości z danych. W przeciwnym razie możesz przeliczyć niektóre słowa zbyt wiele razy, jeśli występują zarówno jako klucze, jak i wartości.

Aby wyodrębnić wszystkie klucze:

jq -r '..|objects|keys[]' <file.json

To rekurencyjnie sprawdza, czy bieżąca rzecz jest obiektem, a jeśli tak, to wyodrębnia klucze. Wynikiem będzie lista kluczy, po jednym w wierszu.

Aby wyodrębnić wszystkie wartości:

jq -r '..|scalars' <file.json

Działa to w podobny sposób, ale ma mniej kroków.

Następnie możesz potokować wynik powyższego grep -c 'PATTERN'(aby dopasować jakiś wzorzec do kluczy lub wartości), lub grep -c -w -F 'WORD'(aby dopasować słowo w kluczach lub wartościach), lub grep -c -x -F 'WORD'(aby dopasować pełny klucz lub wartość), lub podobny, do licz swoje.


0

Mam JSON z czymś takim: "number":"OK","number":OK"powtarzane wiele razy w jednym wierszu.

Mój prosty licznik „OK”:

sed "s|,|\n|g" response | grep -c OK


-1

Użyłem poniżej polecenia awk, aby znaleźć liczbę wystąpień

przykładowy plik

plik kota 1

praveen ajay 
praveen
ajay monkey praveen
praveen boy praveen

Komenda:

awk '{print gsub("praveen",$0)}' file1 | awk 'BEGIN{sum=0}{sum=sum+$1}END{print sum}'

wynik

awk '{print gsub("praveen",$0)}' file1 | awk 'BEGIN{sum=0}{sum=sum+$1}END{print sum}'

5

Lub po prostu awk '{sum+=gsub("praveen","")} END {print sum+0}'.
G-Man mówi „Reinstate Monica”

Daj mi znać, dlaczego głosuj w dół na moją odpowiedź
Praveen Kumar BS
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.