Esiste un modo semplice per contare i caratteri in parole nel file, dal terminale?


Risposte:


20
$ awk '{ print length }' file | sort -n | uniq -c | awk '{ printf("%d character words: %d\n", $2, $1) }'
2 character words: 3
5 character words: 1
7 character words: 1

Il primo awkfiltro stamperà solo la lunghezza di ogni riga nel file chiamato file. Suppongo che questo file contenga una parola per riga.

Il sort -n(ordina le linee dall'output di awknumericamente in ordine crescente) e uniq -c(conta il numero di volte in cui ogni riga si presenta consecutivamente) creerà quindi il seguente output da quello per i dati dati:

   3 2
   1 5
   1 7

Questo viene quindi analizzato dal secondo awkscript che interpreta ogni riga come "Numero X di righe con caratteri Y" e produce l'output desiderato.


La soluzione alternativa è fare tutto in awke mantenere conteggi di lunghezze in un array. È un compromesso tra efficienza, leggibilità / facilità di comprensione (e quindi manutenibilità) quale soluzione è la "migliore".

Soluzione alternativa:

$ awk '{ len[length]++ } END { for (i in len) printf("%d character words: %d\n", i, len[i]) }' file
2 character words: 3
5 character words: 1
7 character words: 1

Non è necessario ordinare in awk (gli array indicizzati numericamente sono ordinati per impostazione predefinita) (più veloce).
Isaac,

@ Freccia lo so. Ho la soluzione commentata nella mia risposta perché Sundeep mi ha battuto con esso in pochi secondi. Alludo anche a questo con il mio ultimo paragrafo.
Kusalananda

Credo che il commento dovrebbe essere utile agli utenti delle soluzioni (non incluso nella tua risposta (o Sundeep) :-) ...). Altrimenti: includi un commento con lo stesso effetto nella tua risposta e rimuoverò felicemente i miei commenti. :-)
Isaac

10

Un altro modo di fare tutto da awksolo

$ awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' ip.txt 
2 character words - 3
5 character words - 1
7 character words - 1
  • words[length()]++ utilizzare la lunghezza della riga di input come chiave per salvare il conteggio
  • END{for(k in words)print k " character words - " words[k]} dopo che tutte le righe sono state elaborate, stampare il contenuto dell'array nel formato desiderato


Confronto delle prestazioni, i numeri selezionati sono i migliori di due corse

$ wc words.txt
 71813  71813 655873 words.txt
$ perl -0777 -ne 'print $_ x 1000' words.txt > long_file.txt
$ du -h --apparent-size long_file.txt
626M    long_file.txt

$ time awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m20.632s
user    0m20.464s
sys     0m0.108s

$ time perl -lne '$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}' long_file.txt > t2

real    0m19.749s
user    0m19.640s
sys     0m0.108s

$ time awk '{ print length }' long_file.txt | sort -n | uniq -c | awk '{ printf("%d character words - %d\n", $2, $1) }' > t3

real    1m23.294s
user    1m24.952s
sys     0m1.980s

$ diff -s <(sort t1) <(sort t2)
Files /dev/fd/63 and /dev/fd/62 are identical
$ diff -s <(sort t1) <(sort t3)
Files /dev/fd/63 and /dev/fd/62 are identical

Se il file ha solo caratteri ASCII,

$ time LC_ALL=C awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m15.651s
user    0m15.496s
sys     0m0.120s

Non so perché il tempo perlnon sia cambiato molto, probabilmente la codifica deve essere impostata in altro modo


L'ho appena aggiunto alla mia soluzione. L'ho eliminato quando ho visto il tuo, però. :-)
Kusalananda

sì, stavo discutendo di cancellare il mio prima di rivedere la tua modifica :)
Sundeep

Non è necessario ordinare un array indicizzato numericamente . È sempre ordinato con un indice crescente. (beh, almeno in awk :-))
Isaac,

lengthsenza ()funziona perfettamente bene qui, quindi potrebbe essere ridondante aggiungere parentesi graffe. Sto usando GNU awk, comunque.
Sergiy Kolodyazhnyy,

2
@SergiyKolodyazhnyy yup, dice il manuale di gnu awkIn older versions of awk, the length() function could be called without any parentheses. Doing so is considered poor practice, although the 2008 POSIX standard explicitly allows it, to support historical practice. For programs to be maximally portable, always supply the parentheses
Sundeep

5

Ecco un perlequivalente (con - opzionale - ordina):

$ perl -lne '
    $h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}
' file
2 character words - 3
5 character words - 1
7 character words - 1

Se gli indici delle chiavi sono numerici: l'array di chiavi deve essere ordinato in Perl?
Isacco,

1
@Arrow: questa risposta sta usando un hash (cioè un array associativo con chiavi stringa), e quelli hanno un ordine delle chiavi indefinito, quindi sì. In effetti, la risposta è leggermente errata perché sta ordinando le chiavi come stringhe, non come numeri. Aggiungendo {$a<=>$b}dopo il sortrisolverlo. In alternativa, si potrebbe usare un array normale con tasti numerici e saltare qualsiasi tasto in cui il valore è zero / indefinito.
Ilmari Karonen,

@IlmariKaronen Grazie, meglio adesso. Che differenza fanno le parentesi graffe !!
Isaac,

Sarebbe più efficiente utilizzare un array anziché un hash. L'OP vuole milioni di righe, quindi qualsiasi sovraccarico di controllo e salto degli zeri durante la stampa è facilmente compensato da una indicizzazione più economica.
Peter Cordes,

5

Un'alternativa una chiamata a GNU awk, utilizzando printf :

$ awk 'BEGIN { PROCINFO["sorted_in"] = "@ind_str_asc"}
       {c[length($0)]++}
       END{
           for(i in c){printf("%s character words - %s\n",i,c[i])}
          }' infile
2 character words - 3
5 character words - 1
7 character words - 1

L'algoritmo core raccoglie solo i conteggi dei caratteri in un array. La parte finale stampa i conteggi raccolti formattati con printf.

Veloce, semplice, una sola chiamata per awk.

Per essere precisi: un po 'più di memoria viene utilizzata per mantenere l'array.
Ma non viene chiamato alcun ordinamento (gli indici di matrici numeriche sono impostati per essere sempre attraversati ordinati verso l'alto con PROCINFO) e solo un programma esterno:, awkanziché diversi.


1
for inpuò capitare di dare indici di array numerici in ordine numerico almeno per alcuni valori o in alcune implementazioni awk, ma ciò non è necessario, non tradizionale e sicuramente non universale. Succede spesso per piccoli set come 2 o 3 o forse 4; prova 10 o 20 su ogni awk a cui hai accesso (senza PROCINFO o WHINY_USERS in gawk) e scommetto $ 50 che almeno un caso non è risolto.
dave_thompson_085,

Grazie per il tuo contributo. Usando questo : credo che sia ordinato ora. :-)
Isaac,

1
@ind_str_ascordina come stringhe, che saranno corrette per i numeri solo se sono tutti a cifra singola (come nel tuo esempio); utilizzare @ind_num_ascse (qualsiasi) valori possono essere 10 o più. E sebbene sia meno un problema ora di quanto non lo fosse prima, questa funzionalità è solo gawk 4.0 .
dave_thompson_085
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.