Scarica esercizi sulle stringhe e più Esercizi in PDF di Programmazione C solo su Docsity!
Raccolta di 5 esercizi svolti in C
sulle stringhe
Premessa
In questo documento trovate 5 esercizi svolti sulle stringhe in C.
I prerequisiti sono:
- variabili
- conoscenza della dualità tipo char e int del C
- operatori logici
- array e indirizzamento dei loro elementi
- cicli for e while
Prima del codice completo con la risposta troverete il ragionamento svolto su
come si arriva effettivamente alla soluzione. Sono presenti anche alcuni
approfondimenti in Python e C++ legati agli esercizi.
Buono studio!
Esercizio 1
Consegna
Data una stringa (array di tipo char) chiamata array, covertire i caratteri dalla a (A) alla
z (Z) da minuscolo a maiuscolo e viceversa.
Suggerimento: prendi una tabella ASCII e fai la differenza in numero decimale tra il
carattere a e A. Guarda se esiste un pattern anche per le altre lettere.
Esempio:
stringa = "thiS Is a WORD!" risultato = "THIs iS A word!"
Sovrascrivere il risultato in array usando questa traccia:
Franco Masotti, 2023 | https://blog.franco.net.eu.org | 1
#include #include #include
int main ( void ){ char array[ 100 ] = "thiS Is a WORD!";
/* Vostro codice */
// Controllo se l'algoritmo funziona correttamente. assert(strcmp(array, "THIs iS A word!") == 0 );
return 0 ; }
Figure 1. Una tabella ASCII, licenza CC
Soluzione
Esistono vari metodi per la soluzione di questo esercizio.
Come scritto nel suggerimento, se guardiamo nella tabella ASCII sopra, la
rappresentazione in numeri decimali dei caratteri a e A sono rispettivamente 97 e 65.
97 - 65 = 32. Proseguiamo con b e B: 98 - 66 = 32. Questo pattern vale fino a z e Z. Questo
significa che per andare da minuscolo a maiuscolo dobbiamo sottrarre 32 alla
rappresentazione decimale. Per contro, se dobbiamo andare da maiuscolo a minuscolo
dobbiamo sommare 32.
Incominciamo a impostare la soluzione. Sappiamo che da qualche parte dobbiamo fare
l’operazione di conversione. Ovviamente bisogna anche iterare tutta la stringa.
Partiamo con questo pseudocodice:
2 | Franco Masotti, 2023 | https://blog.franco.net.eu.org
return 0 ; }
① Importiamo assert.h per verificare la correttezza dell’algoritmo
② string.h ci serve per chiamare strcmp
③ Iteriamo tutta la stringa
④ Troviamo le minuscole e facciamo la conversione
⑤ Troviamo le maiuscole e facciamo la conversione
⑥ La funzione assert permette di verificare se il nostro algoritmo funziona. Viene
spesso usata in fase di debug. Se quello che si trova dentro assert è falso il
programma termina immediatamente con un errore. La funzione strcmp confronta
una stringa con un altra. Se le due stringhe in input sono uguali la funzione ritorna
0. Quindi, usando assert assieme a strcmp possiamo verificare di aver fatto tutto
correttamente
NOTE
Per risolvere questo esercizio è anche possibile utilizzare le funzioni
isupper, islower, toupper, tolower definite in ctype.h. L’approfondimento
qui sotto, anche se in C++, riporta un’altra soluzione che utilizza queste
funzioni.
Approfondimento
Qui sotto trovate la soluzione in C++ usando alcune funzioni integrate. La logica è la
stessa della soluzione originaria in C.
#include #include
int main ( void ) { std::string array = "thiS Is a WORD!"; int i = 0 ;
while (array[i] != '\0') { if (isupper(array[i])) { array[i] = tolower(array[i]); }
else if (islower(array[i])) { array[i] = toupper(array[i]); }
i++; }
4 | Franco Masotti, 2023 | https://blog.franco.net.eu.org
std::cout << array;
assert(array.compare("THIs iS A word!") == 0 );
return 0 ; }
Esercizio 2
Consegna
Data una stringa (tipo char *) chiamata array, invertirla e salvare il risultato in result.
Non è consentito sapere a priori la lunghezza, quindi non si può usare la funzione
strlen oppure codificare la lunghezza direttamente nel codice.
Per esempio:
array = "Questa e' una stringa di prova" result = "avorp id agnirts anu 'e atseuQ"
Usare questa traccia
#include
int main ( void ) { char array[ 100 ] = "Questa e' una stringa di prova"; char result[ 100 ];
/* Vostro codice */
printf("%s\n", result);
return 0 ; }
Soluzione
Una possibile soluzione è quella di calcolare la lunghezza con un ciclo while e poi
leggere dalla fine della stringa per proseguire al contrario usando un ciclo for.
int length = 0 , i;
while ... length++;
Franco Masotti, 2023 | https://blog.franco.net.eu.org | 5
④ Aggiungo il carattere terminatore alla fine della stringa risultato
Soluzione alternativa
È anche possibile invertire una stringa senza calcolare a priori la lunghezza ma
comunque scorrendola normalmente dall’inizio fino alla fine. Quando si è giunti alla
fine si scorre al contrario salvando carattere per carattere in result.
#include
int main ( void ) { char array[ 100 ] = "Questa e' una stringa di prova"; char result[ 100 ];
int i = 0 , j = 0 , done = 0 , go_back = 0 ;
while (!done) { if (array[i] == '\0') ① { go_back = 1 ; i--; }
if (go_back) { result[j] = array[i]; ② j++; ③ i--; } else i++; ④
if (go_back && i == -1 ) ⑤ done = 1 ; }
result[j] = '\0';
printf("%s\n", result);
return 0 ; }
① Una volta arrivati in fondo alla stringa, si va indietro di una posizione per saltare il
carattere terminatore. Setto anche il flag go_back che dice di spostare l’indice
indietro.
② Salvo un carattere per volta nella stringa result
③ Proseguo sulla casella successiva di result e sulla casella precendente di array
Franco Masotti, 2023 | https://blog.franco.net.eu.org | 7
④ Proseguo sulla casella successiva di array perché devo ancora arrivare alla fine della
stringa.
⑤ Setto il flag done per uscire dal ciclo. In questo modo si evita di andare in underflow
leggendo la stringa originale array
Esercizio 3
Consegna
Modificare l’esercizio precedente per fare in modo che la stringa venga letta
dall’utente all’interno di un ciclo.
Quando l’utente inserisce la stringa EOF il programma deve terminare e si deve
stampare a video il programma e' terminato.
Non è consentito usare funzioni quali strcmp, strlen, ecc…
Potete usare la traccia precedente.
Soluzione
Possiamo estendere la soluzione precedente mettendola all’interno di un ciclo while,
così:
while
Per sapere se l’utente ha inserito esattamente "EOF\0" si può procedere carattere per
carattere. Non sapendo a priori la lunghezza della stringa bisogna controllare di non
raggiungere il carattere terminatore '\0'.
Possiamo migliorare lo pseudocodice precedente con questo:
done = false;
while (!done) { scanf ...
if (array[0] != '\0' && array[0] == 'E' && array[1] != '\0' && array[1] == 'O' && array[2] != '\0' && array[2] == 'F'
8 | Franco Masotti, 2023 | https://blog.franco.net.eu.org
return 0 ; }
① Rimaniamo all’interno del ciclo finchè non viene settato il flag done
② Usiamo il classico scanf
③ Controllo se l’utente ha immesso esattamente "EOF\0"
④ Il codice all’interno dell' if è uguale a quello dell’esercizio precedente.
WARNING
Usando scanf non c’è nessun controllo di overflow con la stringa!
Infatti inserendo troppi caratteri si incappa in un errore di
segmentazione. Inoltre la funzione scanf con %s ignora gli spazi
prima e dopo la stringa. In generale scanf non è una funzione
sicura ma è comunque adatta per spiegare questi problemi
introduttivi.
Esercizio 4
Consegna
Data una stringa controllare se è palindroma.
Una parola palindroma si legge allo stesso modo da sinistra a destra e da destra a
sinistra: onorarono, per esempio, è una parola palindroma.
Il programma non deve prevedere la gestione dei caratteri whitespace: per esempio
onora rono non deve risultare come palindromo.
La funzione main deve ritornare true se è palindroma oppure false se non lo è. Questo
viene fatto salvando il risultato nella variabile booleana palindrome.
Le variabili booleane in C sono definite in stdbool.h e sono di tipo bool
Infine è necessario stampare:
- la stringa e' palindroma
- la stringa non e' palindroma
a seconda dei casi.
Usare questa traccia:
#include
10 | Franco Masotti, 2023 | https://blog.franco.net.eu.org
#include
bool main ( void ) { // La stringa e' di 128 caratteri + 1 carattere terminatore di stringa. char array[ 129 ] = "EaQDQeLCUsddXqFOUjFMcFPUxNZzdtuctIIKZzOmDyxHoNDbqtaT" "oBAbWBCoIqDzzDqIoCBWbABoTatqbDNoHxyDmOzZKIItcutdzZNx" "UPFcMFjUOFqXddsUCLeQDQaE" bool palindrome;
/* Vostro codice */
return palindrome; }
Soluzione
Per sapere se una parola è palindroma possiamo partire dal caso base di una stringa da
una lettera: a per esempio è palindroma perché pur essendo di una lettera solo si legge
sempre allo stesso modo in tutti i versi.
Proviamo ora con la stringa dacad e impostiamo due indici i e j in questo modo: i parte
sempre da 0 mentre j è sempre l’ultimo elemento della stringa (per semplicità
ignioriamo il terminatore '\0').
array = [d, a, c, a, d] | | i j
La domanda da fare è: array[i] == array[j]? Sì, allora proseguiamo così: facciamo
avanzare i (i++) e regredire j (j--):
array = [d, a, c, a, d] | | i j
Facciamo la stessa domanda di prima: array[i] == array[j]? il risultato è ancora sì.
Facciamo un altro passaggio:
array = [d, a, c, a, d] | i,j
Se i == j allora ci troviamo nel caso base esposto precedentemente: una stringa di una
lettera soltanto è per definizione palindroma. Quindi, con questo metodo, sappiamo
che dacad è palindroma.
Franco Masotti, 2023 | https://blog.franco.net.eu.org | 11
else { printf("la stringa non e' palindroma\n"); }
return palindrome; }
① Conto la lunghezza della stringa meno 1 perché il conteggio parte da 0 e non da 1
② Evito di contare il carattere terminatore '\0'
③ Continuo a iterare sulla stringa se i due caratteri in analisi sono uguali.
④ Se i due caratteri in analisi sono diversi allora la stringa sicuramente non è
palidroma: bisogna uscire dal ciclo e dal programma.
⑤ Se i == j allora significa che tutti i caratteri fino al centro della stringa sono
speculari, quindi la stringa è palindroma. L’altro caso, quando i > j, si verifica con
una stringa di lunghezza pari. Potete provare l’algoritmo con la stringa dccd.
Arrivando a uno di questi due casi il ciclo deve terminare e il programma deve
uscire stampando la stringa e' palindroma
Con la stringa fornita la viene stampato a video la stringa e' palindroma e la funzione
main deve ritornare 1.
Approfondimento
Generazione di una stringa casuale di 128 caratteri palindroma in Python.
>>> import secrets , string >>> left = ''.join([secrets.choice(string.ascii_letters) for i in range( 0 , 64 )]) ① >>> left + left[::- 1 ] ② 'EaQDQeLCUsddXqFOUjFMcFPUxNZzdtuctIIKZzOmDyxHoNDbqtaToBAbWBCoIqDzzDqIoCBWbABoTatqbDNoHxyDmOzZKI ItcutdzZNxUPFcMFjUOFqXddsUCLeQDQaE'
① Genero una stringa di 64 lettere minuscole e maiuscole casuali
② Concateno la stringa originale con il suo inverso
Esercizio 5
Scrivete un piccolo parser in C per HTML.
Fate in modo che il programma trasformi i tag
Franco Masotti, 2023 | https://blog.franco.net.eu.org | 13
ignorando altri tag e senza calcolare a priori la lunghezza della stringa.
Usando questa stringa di esempio:
< h1 >Questo e' un titolo < p >Questo e' un po' di < em >contenuto
deve diventare:
< h2 >Questo e' un titolo < p >Questo e' un po' di < b >contenuto
Savlate il risultato nell’array result. Usate questa traccia:
#include
int main ( void ) { char array[ 100 ]= "Questo e' un titolo\n
Questo e' un po' di contenuto
";
char result[
100 ];
/* Vostro codice */
return 0 ; }
IMPORTANT
La soluzione non può prevedere l’indirizzamento diretto
dell’array per fare le sostituzioni, per esempio:
char array[ 100 ] = "test char result[ 100 ];
result[ 0 ] = array[ 0 ] result[ 1 ] = array[ 1 ] result[ 2 ] = '2' result[ 3 ] = array[ 3 ] // ecc... result[ 11 ] = '2'
NOTE
Guardate l’array un elemento per volta stando attenti alla fine della
stringa. Dovere trovare l’inizio dei tag HTML (il carattere <) e capire se il
tag in questione è tra quelli da convertire.
14 | Franco Masotti, 2023 | https://blog.franco.net.eu.org
k,l
result = [<] | j
// Quindi alla fine del ciclo diventa cosi'.
array = [<,h,1,>] | | k l
result = [<] | j
// Quindi e' sicuramente un tag di apertura perche' l - k == 2. // Ora vediamo quello che c'e' nel tag.
array = [<,h,1,>] | k
result = [<] | j
// Il tag incomincia con 'h' quindi forse deve essere cambiato.
array = [<,h,1,>] | k
result = [<,h,2] | j
// Abbiamo trovato un h1 quindi basta cambiarlo in h2.
array = [<,h,1,>] | k
result = [<,h,2,>] | j
// Copia dell'ultimo carattere.
Per trasformare h1 in h2 basta inserire manualmente il carattere 2 al posto di 1 invece
che copiare i caratteri esistenti come negli altri casi. Un discorso simile vale per
trasformare em in b. In questo caso però bisogna anche saltare l’incremento dell’indice
j al momento della copia in result.
16 | Franco Masotti, 2023 | https://blog.franco.net.eu.org
#include
int main ( void ) { char array[ 100 ]= "Questo e' un titolo\n
Questo e' un po' di contenuto
";
char result[
100 ];
result[ 99 ] = '\0'; ① int i = 0 , j = 0 , k = 0 , l = 0 , valid_closing_tag = 0 ;
while (array[i] != '\0') { k = i;
result[j] = array[k];
if (array[k] == '<') ② { k++; j++;
if (array[k] != '\0' && array [k] == '/') ③ { result[j] = array[k]; k++; j++; } }
if (k > i) ④ { ⑤ l = k; valid_closing_tag = 0 ; while (valid_closing_tag == 0 && array[l] != '\0') { if (l - k == 2 && array[l] == '>') { valid_closing_tag = 1 ; } l++; }
if (valid_closing_tag == 1 && array[k] != '\0' && array[k] == 'h') ⑥ { result[j] = array[k]; k++; j++;
if (array[k] != '\0' && array[k] == '1') ⑦ { result[j] = '2'; j++; k++;
Franco Masotti, 2023 | https://blog.franco.net.eu.org | 17
⑨ Trovo l' e di em
⑩ In questo punto NON avanzo j perché da em, che è composto da due caratteri,
passiamo a b che è di un solo carattere
⑪ Trovo la m di em
⑫ Caso base: non è stato trovato nessun tag HTML all’indice corrente
NOTE
L’indice i ci dice in generale dove siamo arrivati ad iterare sulla stringa
originale alla fine di ogni ciclo, mentre k può venire incrementato più
volte all’interno del ciclo in base alle condizioni. Vedere anche il punto
WARNING
Alcuni controlli sono incompleti: manca il confronto tra opening
tag e closing tag che dovrebbero essere uguali se corrispondono allo
stesso livello.
Franco Masotti, 2023 | https://blog.franco.net.eu.org | 19