Caratteri e stringhe Caratteri Dato che un computer può memorizzare esclusivamente sequenze di bit, per memorizzare un carattere (e quindi testi) è necessario stabilire una convenzione che associa a un carattere una determinata sequenza di bit, rappresentata ad esempio con un numero intero Lo standard principale si chiama codice ASCII (American Standard Code for Information Interchange), pronunciato aski (/ˈæski/) Il codice ASCII standard stabilisce la corrispondenza tra 128 caratteri e un numero da 0 a 127 (7 bit) 1
Caratteri Codice ASCII Caratteri 0-31 Caratteri di controllo 32 spazio 33-47 Altri caratteri (!, ", #, $, %, ', (, ), *, +, -, /,.,,) 48-57 Cifre da 0 a 9 58-64 Altri caratteri (:, ;, <, =, >,?, @) 65-90 Lettere maiuscole (A-Z) 91-96 Altri caratteri ([, \, ], ^, _, `) 97-122 Lettere minuscole (a-z) 123-127 Altri caratteri ({,,, ~) Caratteri In C per indicare un carattere si usano gli apici singoli Es.: 'A' è il carattere A maiuscolo (a cui corrisponde il numero 65 nel codice ASCII) '0' è il carattere della cifra zero (a cui corrisponde il numero 48 nel codice ASCII) 0, senza apici, invece, è il numero zero, diverso dal carattere '0' 2
Caratteri Il tipo char è utile per memorizzare un carattere Dato che per memorizzare un carattere il C memorizza il suo codice ASCII, una variabile di tipo char può essere usata sia come numero intero che come carattere Es.: char c = 'A'; e char c = 65; sono equivalenti Dato char d = '3', posso ottenere il suo valore con d-'0', che il C eseguirà come 51-48, cioè 3 Stringhe In C non esiste il tipo stringa Una stringa di caratteri in C è un array di caratteri di una certa dimensione terminato dal carattere '\0' Un array di N caratteri può dunque ospitare stringhe lunghe al più N 1 caratteri perché una cella è destinata al simbolo '\0' 0 1 2 3 4 5 6 7 char s[8]; m a t t o n e \0 3
Manipolazione di stringhe Il C fornisce alcune funzioni per la manipolazione delle stringhe i cui prototipi sono nell header string.h. Tra queste funzioni: size_t strlen(char *str); strlen sta per string length e restituisce il numero di caratteri di cui è composta una stringa (size_t è un tipo intero senza segno; a seconda dell architettura e del compilatore, è un unsigned int o un unsigned long) Es.: #include <string.h> int l; char s[] = "pippo"; l = strlen(s); // l vale 5 Manipolazione di stringhe char* strcpy(char* dst, char* src); strcpy sta per string copy ed effettua una copia tra stringhe. Il primo parametro è la stringa di destinazione e il secondo è la stringa sorgente. Il valore di ritorno di strcpy si usa raramente (è un puntatore allo '\0' finale di dst) Es.: #include <string.h> char s1[10], s2[10]; strcpy(s1, "pippo"); strcpy(s2, s1); N.B.: in C non si può usare una stringa in un assegnamento (s = "stringa") con la sola eccezione della dichiarazione e inizializzazione (char s[] = "stringa";) 4
Manipolazione di stringhe int strcmp(char *s1, char *s2); strcmp sta per string compare e confronta due stringhe. Restituisce 0 se le stringhe sono uguali, un numero diverso da 0 se sono diverse (più precisamente negativo se s1 precede s2 lessicograficamente, cioè considerando l ordinamento ASCII, e positivo se s1 segue s2) Es.: #include <string.h> int cmp; char str1[] = "Pippo", str2[] = "pippo"; cmp = strcmp(str1, str2); //cmp è un numero negativo perché 'P'<'p' Leggere un singolo carattere: Stampare un singolo carattere: Esempio: Perché abbiamo usato un int per la variabile c? 5
Leggere e scriviamo finché incontriamo l end-of-file while (c!= EOF) { //EOF è definita in stdio.h Trovare almeno altri due modi per scrivere il ciclo 1: for ( c!= EOF; c = getchar()) 6
2: while (1) { if (c == EOF) break; 3: for (;;) { if (c == EOF) break; 7
4: while ((c = getchar())!= EOF) Scriviamo un programma toupper.c che converta un testo in maiuscolo usando le funzioni getchar, putchar e ciò che abbiamo detto sul tipo char e la codifica ASCII 8
Convertiamo un testo in maiuscolo (toupper.c) while (c!= EOF) { if (c >= 'a' && c <= 'z') c = c - ('a' - 'A'); Usando le funzioni di libreria islower e toupper in ctype.h: #include <ctype.h> while (c!= EOF) { if (islower(c)) //in realtà è sufficiente usare direttamente la funzione toupper c = toupper(c); 9
Usiamo il programma per rendere maiuscolo il sorgente stesso toupper < toupper.c Se generiamo un nuovo sorgente in questo modo e compiliamo, cosa succede? toupper < toupper.c > pippo.c gcc -o pippo pippo.c I/O di stringhe Input di una stringa char stringa[8]; scanf("%s", stringa); N.B.: In questo caso la variabile non è preceduta da & (i motivi saranno chiari in seguito) Nell input di stringhe, la scanf scarta automaticamente i white space iniziali e continua a leggere fino a quando incontra un white space (quindi non si può usare la scanf in questa forma per leggere una stringa composta da più di una parola) Attenzione: cosa succede se l utente inserisce una stringa con 10 caratteri? 10
I/O di stringhe Il C di norma non effettua controlli. Continua a scrivere in memoria anche oltre la fine dell array: si ha un buffer overflow 0 1 2 3 4 5 6 7 char stringa[8]; p r o g r a m m a \0 In questo modo scanf potrebbe sovrascrivere altre strutture dati oppure scrivere in una zona di memoria in cui il programma non ha il permesso di scrivere (il runtime può dare l errore di segmentation fault). I/O di stringhe Per prevenire questi errori bisogna limitare il numero di caratteri letti in input: scanf("%7s", stringa); In questo modo scanf interrompe la lettura dopo al massimo 7 caratteri (l array ha capienza 8 ma dobbiamo lasciare un carattere per lo '\0' finale). Eventuali caratteri non letti rimangono nel buffer 11
I/O di stringhe Per l input di stringhe, la funzione scanf ha due limitazioni: Termina la lettura al primo whitespace Non è agevole limitare il numero di caratteri in input usando il valore di una variabile o una costante simbolica Per questi motivi può essere utile usare, invece, la funzione fgets, che legge righe di testo e accetta come parametro anche la dimensione del vettore di caratteri I/O di stringhe Per utilizzare fgets occorre include stdio.h char* fgets(char *str, int size, FILE *stream); str è l array di char in cui memorizzare la stringa (che può contenere anche spazi) size è la dimensione dell array (quindi la lettura termina al massimo dopo size-1 caratteri letti in modo da lasciare un elemento nell array per lo '\0' finale) stream nel caso di input da tastiera è sempre impostato come stdin (vedremo più in là la lettura da file) In caso di errore restituisce NULL 12
I/O di stringhe Nota: a differenza di scanf, fgets conserva al termine della stringa il carattere newline ('\n') (ad eccezione del caso in cui l utente inserisca una stringa più lunga di size 1) Quindi per leggere una stringa con fgets si può scrivere così: char str[size]; if (fgets(str, SIZE, stdin) == NULL) { //trattamento dell errore if (strlen(str) > 0 && str[strlen(str)-1] == '\n') str[strlen(str)-1] = '\0'; //rimozione dell eventuale \n finale Matrici e vettori di stringhe 13
Matrici Le matrici sono array bidimensionali Definizione: tipo nome[numero_righe][numero_colonne]; L indice per le righe va da 0 a numero_righe 1, quello delle colonne da 0 a numero_colonne 1 Es.: int matr[3][6]; definisce una matrice matr di 3 righe (con indice da 0 a 2) e 6 colonne (con indice da 0 a 5) Matrici Una matrice viene interpretata dal C come un vettore di vettori Quindi int matr[3][6]; definisce un vettore di 3 elementi ognuno dei quali è un vettore di 6 interi Vengono memorizzati per righe matr[0][4] matr[2][3] 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 matr[0] matr[1] matr[2] 14
Vettori di stringhe Un vettore di stringhe, quindi, corrisponde a una matrice di caratteri Esempio: char elenco[10][100]; definisce un vettore di 10 stringhe lunghe ognuna al massimo 99 caratteri Ogni riga della matrice si può usare come una stringa. Ad es. strcpy(elenco[2], "latte"); scanf("%99s", elenco[9]); 15