2011-08-24 16 views
5

Sono in grado di trovare il numero di volte che una parola si verifica in un file di testo come in Linux possiamo utilizzarecome trovo il conteggio di più parole in un file di testo?

cat filename|grep -c tom 

mia domanda è come faccio a trovare il conteggio di più parole come "Tom" e "joe "in un file di testo.

+0

grep conta le linee, non le parole. Una linea con 'tomtom' su di essa conta come uno o due? – tchrist

+0

Che cosa vuoi esattamente? Conteggi multipli, uno per ogni parola che hai specificato? Somma dei conteggi per tutte le parole che hai specificato? Che cos'è la "parola"? Come già menzionato da Tchrist, il tuo esempio conta il numero di righe che corrispondono a un'espressione regolare, non il numero di parole. – GreyCat

risposta

3

Dal momento che si dispone di un nome di coppia, le espressioni regolari è il modo di andare avanti questo. All'inizio ho pensato che fosse semplice come un conteggio di grep sull'espressione regolare di joe o tom, ma direi che questo non ha tenuto conto dello scenario in cui tom e joe sono sulla stessa linea (o tom e tom per quella materia) .

test

.txt:

tom is really really cool! joe for the win! 
tom is actually lame. 


$ grep -c '\<\(tom\|joe\)\>' test.txt 
2 

Come si può vedere dal file test.txt, 2 è la risposta sbagliata, quindi abbiamo bisogno di tenere conto di nomi che sono sulla stessa linea.

Ho quindi utilizzato grep -o per mostrare solo la parte di una linea corrispondente che corrisponde al modello in cui ha fornito le corrispondenze modello corrette di tom o joe nel file. Ho quindi convogliato i risultati in numero di righe in wc per il conteggio delle righe.

$ grep -o '\(joe\|tom\)' test.txt|wc -l 
     3 

3 ... la risposta corretta! Spero che questo aiuti

+2

E riguardo 'tomtom'? – tchrist

+0

Ho modificato un po 'l'espressione regolare per gestire il caso del tomtom. Bel test case ... grazie per avermelo fatto notare. –

+0

Il test davvero difficile implicherebbe corrispondenze sovrapposte sulle parole originali. :) Ad esempio, se le parole che volevi contare erano 'cure',' nucleo', 'contare',' lisi', 'isola',' terra', e 'piatto', allora si otterrebbero 2 colpi cose come 'insicure 'e' stravaganti' e 3 successi su cose come 'islandish' e' corelysis'. Un approccio ingenuo conterebbe quelli come solo uno a testa. Questo non è divertente con una regex, ma piuttosto facile con N di loro, uno per parola. – tchrist

2

Ok, quindi prima dividere il file in parole, poi sort e uniq:

tr -cs '[:alnum:]' '\n' < testdata | sort | uniq -c 

Si utilizza uniq:

sort filename | uniq -c 

+0

Parole, ** non ** linee. –

+1

Whoops. Per la prossima volta, che ne dici di aver letto correttamente la domanda? * facepalm * – carlpett

+0

questa (suddivisione in parole, selezione, conteggio) sarebbe stata anche la mia opzione. quando sostituisci cose che non sono ': alnum:' a '\ n' potresti aver bisogno di occuparti delle differenze linguistiche, come in' cat Castilian/* .txt | tr A-Z a-z | tr -cs '[a-záóúíéñ'] '' \ n '| ordinare | uniq -c | sort -n' – mariotomo

1

Uso awk:

{for (i=1;i<=NF;i++) 
    count[$i]++ 
} 
END { 
    for (i in count) 
     print count[i], i 
} 

Questo produrrà un conteggio di frequenza parola completo per l'input. tubo di uscita tho per grep per ottenere i campi desiderati

awk -f w.awk input | grep -E 'tom|joe' 

BTW, non è necessario cat nel tuo esempio, la maggior parte dei programmi che funge da filtri possono prendere il nome del file come parametro; Quindi è meglio usare

grep -c tom filename 

in caso contrario, v'è una forte possibilità che la gente iniziare a gettare Useless Use of Cat Award a voi ;-)

+0

"la maggior parte dei programmi che fungono da filtri può prendere il nome del file come parametro" ... e anche quando non lo fanno, è comunque possibile utilizzare il reindirizzamento dell'input (come 'grep -c tom

+0

'grep -c' non cerca le parole, quindi devi cercarlo. –

0

Qui è uno:

cat txt | tr -s '[:punct:][:space:][:blank:]'| tr '[:punct:][:space:][:blank:]' '\n\n\n' | tr -s '\n' | sort | uniq -c 

UPDATE

Una soluzione di script shell:

#!/bin/bash 

file_name="$2" 
string="$1" 

if [ $# -ne 2 ] 
    then 
    echo "Usage: $0 <pattern to search> <file_name>" 
    exit 1 
fi 

if [ ! -f "$file_name" ] 
then 
    echo "file \"$file_name\" does not exist, or is not a regular file" 
    exit 2 
fi 

line_no_list=("") 
curr_line_indx=1 
line_no_indx=0 
total_occurance=0 

# line_no_list contains loc k the line number loc k+1 the number 
# of times the string occur at that line 
while read line 
do 
    flag=0 
    while [[ "$line" == *$string* ]] 
    do 
    flag=1 
    line_no_list[line_no_indx]=$curr_line_indx 
    line_no_list[line_no_indx+1]=$((line_no_list[line_no_indx+1]+1)) 
    total_occurance=$((total_occurance+1)) 
# remove the pattern "$string" with a null" and recheck 
    line=${line/"$string"/} 
    done 
# if we have entered the while loop then increment the 
# line index to access the next array pos in the next 
# iteration 
    if ((flag == 1)) 
    then 
    line_no_indx=$((line_no_indx+2)) 
    fi 
    curr_line_indx=$((curr_line_indx+1)) 
done < "$file_name" 


echo -e "\nThe string \"$string\" occurs \"$total_occurance\" times" 
echo -e "The string \"$string\" occurs in \"$((line_no_indx/2))\" lines" 
echo "[Occurence # : Line Number : Nos of Occurance in this line]: " 

for ((i=0; i<line_no_indx; i=i+2)) 
do 
    echo "$((i/2+1)) : ${line_no_list[i]} : ${line_no_list[i+1]} " 
done 

echo 
0
  1. L'esempio che hai dato non non ricerca di parole "tom". Conterà "atomo" e "fondo" e molti altri.
  2. Grep cerca le espressioni regolari . un'espressione regolare che corrisponde parola "tom" o "Joe" è

    \<\(tom\|joe\)\> 
    
0

Si potrebbe fare regexp,

cat filename |tr ' ' '\n' |grep -c -e "\(joe\|tom\)" 
+0

La tua soluzione conta anche per joe e tom sulla stessa linea. Bello! –

+0

@Travis: Tuttavia, conta erroneamente 'tomtom' solo una volta, anche se mio nonno riesce a vedere che ci sono due regali di' tom'. – tchrist

0

ho completamente dimenticato grep -f:

cat filename | grep -fc names 

AWK soluzione:

Supponendo che i nomi sono in un file chiamato names:

cat filename | awk 'NR==FNR {h[NR] = $1;ct[i] = 0; cnt=NR} NR !=FNR {for(i=1;i<=cnt;++i) if(match($0,h[i])!=0) ++ct[i] } END {for(i in h) print h[i], ct[i]}' names - 

Si noti che l'originale grep non cerca le parole. per esempio.

$ echo tomorrow | grep -c tom 
1 

È necessario grep -w

0
gawk -vRS='[^[:alpha:]]+' '{print}' | grep -c '^(tom|joe|bob|sue)$' 

Il programma gawk imposta il separatore di record a qualsiasi cosa non alfabetico, così ogni parola finirà su una riga separata. Allora grep conta le linee che corrispondono a una delle parole che vuoi esattamente.

Utilizziamo gawk perché POSIX awk non consente il separatore di record di espressioni regolari.

Per brevità, è possibile sostituire '{print}' con 1 - in entrambi i casi, si tratta di un programma di Awk che stampa semplicemente tutti i record di input

("è 1 vero è allora fare l'azione di default, che è {print}??".)
0

per trovare tutti i colpi in tutte le linee di

echo "tom is really really cool! joe for the win! 
tom is actually lame." | akw '{i+=gsub(/tom|joe/,"")} END {print i}' 
3 

Questo conterà "TomTom", come 2 risultati.

Problemi correlati