LabASD - Conta il numero di parole distinte

Moreno Marzolla

Ultimo aggiornamento: 2024-03-22

By Willi Heidelbach, CC BY 2.5, https://commons.wikimedia.org/w/index.php?curid=154912
By Willi Heidelbach, CC BY 2.5, https://commons.wikimedia.org/w/index.php?curid=154912

Scrivere un programma che legga un file di testo il cui nome viene specificato sulla riga di comando, e determini il numero di parole e il numero di parole distinte in esso contenute; non si deve fare distinzione fra lettere maiuscole e minuscole, per cui “prova”, “Prova”, “PROVA”, ecc. sono la stessa parola.

Per “parola” si intende una sequenza contigua di caratteri per i quali la funzione

int isalpha(int c)

restituisce true (cioè un valore diverso da zero). Ad esempio, la stringa “i diritti inviolabili dell’uomo” è composta da cinque parole: “i”, “diritti”, “inviolabili”, “dell” e “uomo”.

Quindi, nel testo

La Repubblica riconosce e garantisce i diritti inviolabili dell’uomo, sia come singolo, sia nelle formazioni sociali ove si svolge la sua personalita’, e richiede l’adempimento dei doveri inderogabili di solidarieta’ politica, economica e sociale.

sono presenti le 36 parole seguenti:

la
repubblica
riconosce
e
garantisce
i
diritti
inviolabili
dell
uomo
sia
come
singolo
sia
nelle
formazioni
sociali
ove
si
svolge
la
sua
personalita
e
richiede
l
adempimento
dei
doveri
inderogabili
di
solidarieta
politica
economica
e
sociale

di cui 32 sono distinte.

Conviene leggere le parole un carattere alla volta usando la funzione

int fgetc(FILE *stream)

dichiarata in <stdio.h>; la funzione restituisce il codice ASCII del prossimo carattere presente nel file stream, oppure EOF in caso di fine file; EOF è una costante definita in <stdio.h>.

Per convertire un carattere in minuscolo si può usare la funzione

int tolower(int c)

dichiarata in <ctype.h>. La funzione accetta come parametro il codice ASCII c di un simbolo, e restituisce il codice ASCII del corrispondente carattere minuscolo se c è una lettera dell’alfabeto, oppure restituisce c se si tratta di un altro simbolo.

Si può assumere che tutte le parole abbiano lunghezza strettamente minore a WORDLEN.

Per risolvere il problema si possono salvare le parole come chiavi di una hash table, che è stata oggetto di un altro esercizio. Si consiglia di procedere come segue:

Per compilare:

    gcc -std=c90 -Wall -Wpedantic hashtable.c conta-parole.c -o conta-parole

Per eseguire in ambiente Linux/MacOSX:

    ./conta-parole nome_file_input

Per eseguire in ambiente Windows:

    .\conta-parole nome_file_input

Alcuni punti di attenzione

Si presti attenzione che il linguaggio C non specifica se il tipo char dabba essere signed o unsigned. In altre parole, la dichiarazione char c; lascia libertà ai compilatori di decidere se c rappresenta un valore con segno o senza segno. Nel caso si desideri uno dei due occorre specificarlo esplicitamente (es., unsigned char c definisce c come unsigned).

In alcuni sistemi (tra cui il mio) viene usato il tipo signed; questo significa che il seguente frammento di codice:

char c; /* SBAGLIATO */
FILE *filein = fopen(...);
...
c = fgetc(filein);
if (isalpha(c)) { ... }

non è corretto in presenza di lettere accentate, perché queste hanno un codice ASCII superiore a 127. Ad esempio, se alla variabile c fosse assegnato il carattere 'è' (codice ASCII 195), il valore memorizzato in c sarebbe -61, dato che 195 non può essere rappresentato con 8 bit in complemento a due. Di conseguenza la funzione isalpha(c) riceverebbe un valore negativo producendo un risultato indefinito.

La soluzione consiste nel dichiarare c di tipo int:

int c; /* Corretto */
FILE *filein = fopen(...);
...
c = fgetc(filein);
if (isalpha(c)) { ... }

Si noti che non sarebbe corretto definire c di tipo unsigned char, perché in caso di fine file, la funzione fetc() restituisce EOF che normalmente ha valore -1.

File