Conte o número de palavras que começam com cada letra de um arquivo

1

Eu preciso pegar um arquivo e imprimir o número de vezes que cada letra do alfabeto inicia uma palavra, em ordem decrescente de contagem de palavras. Por exemplo, se o arquivo for:

my nice name is Mike Meller 

a saída deve ser:

3 M
2 N
1 I

Eu preciso fazer isso em uma única linha. Conheço comandos como wc -m e wc -w , mas não tenho certeza de como iterar sobre cada caractere e imprimi-lo da mesma maneira e, em seguida, classificá-lo como quiser.

    
por UFC Insider 02.12.2016 / 20:21

4 respostas

2

Uma maneira ... (editada para evitar contar a mesma palavra duas vezes)

$ echo "my nice name is Mike Meller" | tr ' ' '\n' | sort -f | uniq -i | sed -nr 's/^([a-z]).*/\U/Ip' | uniq -c | sort -r
  3 M
  2 N
  1 I
  • tr ' ' '\n' muda espaços para novas linhas
  • sort -f classifica as linhas para que as mesmas entradas estejam juntas, mesmo se o caso for diferente
  • uniq -i remove palavras duplicadas, ignorando o caso
  • sed -nr 's/^([a-z]).*/\U/Ip' remove tudo, menos a primeira letra, altera todas as letras para maiúsculas e não imprime a linha se não começar com uma letra
  • uniq -c conta as linhas que são as mesmas
  • sort -r sort descendente

(substitua echo "my nice name is Mike Meller" por cat name-of-your-file )

    
por 02.12.2016 / 22:10
1

com perl :

perl -Mopen=locale -lne '
  $c{uc $_}++ for /\b\p{Alpha}/g;
  END{for (sort {$c{$b} <=> $c{$a}} keys %c) {print "$c{$_} $_"}}'

Observe que, se algumas letras aparecerem em um formato decomposto. Por exemplo, se É for inserido como (ou seja, E seguido por um acento de combinação U + 0301) em vez do pré-composto É (U + 00E9), ele será contado como E , não nem É .

Se isso é uma preocupação, então, provavelmente, a melhor abordagem é primeiro decompor o texto (uma vez que alguns graphems não têm um formulário pré-composto) e trabalhar em uma base de cluster graphem. Existem alguns, como , que você provavelmente desejaria detalhar de qualquer maneira:

Compare:

$ printf 'my fine name is \uc9ric, maybe E\u301ric, certainly not Eric\n' |
  perl -Mopen=locale -lne '
    $c{uc $_}++ for /\b\p{Alpha}/g;
    END{for (sort {$c{$b} <=> $c{$a}} keys %c) {print "$c{$_} $_"}}'
2 E
2 N
2 M
1 C
1 FI
1 É
1 I

com:

$ printf 'my fine name is \uc9ric, maybe E\u301ric, certainly not Eric\n' |
  perl -Mopen=locale -MUnicode::Normalize -lne '
    $c{uc $_}++ for NFKD($_) =~ /\b(?=\p{Alpha})\X/g;
    END{for (sort {$c{$b} <=> $c{$a}} keys %c) {print "$c{$_} $_"}}'
2 É
2 M
2 N
1 E
1 I
1 C
1 F
    
por 02.12.2016 / 23:27
1

GNU awk:

gawk '
  { for (i=1; i<=NF; i++) count[toupper(substr($i,1,1))]++ } 
  END {
    PROCINFO["sorted_in"] = "@val_num_desc"
    for (key in count) print count[key], key
  }
' file 
    
por 03.12.2016 / 16:15
0

Não é um projeto de lição de casa, espero? ;-) A parte complicada é que você não quer contar o "L" em Meller duas vezes, certo? Daí o "distinto".

$cat t
my nice name is Mike Meller

E, em seguida, um pipeline de comandos para fazer a transformação:

$tr '[a-z]' '[A-Z]' < t |     # Convert all to upper case
fold -b -w 1 |                # Break into one letter per line
awk -f t.awk |                # Pipe the whole mess to awk to count
sort -r -n                    # Sort in reverse numeric order

O script awk é melhor dividido em um arquivo separado, mas você pode simplesmente colocar tudo em uma linha de base:

$cat t.awk    
/ / {                         # Match spaces,
  for (c in wc) {dc[c]+=1}    #  Accumulate word count (wc) into doc count (dc)
  split("",wc)                #  Reset the word count
}

!/ / {                        # Match non-spaces,
  if (wc[$1] == "") wc[$1]=1  #  If haven't already seen char in this word, mark it Donny
}

# Finally, output the count and the letter
END {
  for (c in wc) {dc[c]+=1}    # Accumulate one last time, in case there is no trailing space
  for (c in dc) {print c, dc[c]}
}

Que produz (para mim) essa saída:

$tr '[a-z]' '[A-Z]' < t | fold -b -w 1 | awk -f t.awk  | sort -r -n
4 M
4 E
3 I
2 N
1 Y
1 S
1 R
1 L
1 K
1 C
1 A
    
por 02.12.2016 / 21:32