Le Stringhe in C++ (CAP 3, parte I) Alberto Garfagnini e Marco Mazzocco Università degli studi di Padova A.A. 2014/2015 Indice Dichiarazioni using per il namespace Lo specificatore di tipo decltype Il tipo string della libraria standard del C++: ovvero supporto di stringhe di caratteri a lunghezza variabile Il namespace std della Libreria Standard del C++ Per accedere agli oggetti definiti nella libreria standard del C++ abbiamo specificato il namespace std L operatore di visibilità :: dice al compilatore di cercare l oggetto nel dominio specificato a sinistra dell operatore regione di appartenenza degli oggetti La dichiarazione using permette di accedere ad un oggetto nel namespace senza specificarlo con la solita sintassi namespace_name::prefix int using std::cout std::cin operatore di scopo cout << "Hello!" nome dell oggetto La dichiarazione using Per ogni oggetto presente in un namespace che vogliamo usare, è necessaria una dichiarazione using separata : using std::cout; using std::endl; using std::cin; cout << "Dammi un numero:"; int numero = 0; cin >> numero; cout << "Numero inserito: "; cout << numero << endl; È possibile rendere visibile al programma tutti gli oggetti e le classi di un namespace con la dichiarazione using namespace namespace_name; using namespace std; per il namespace delle librerie standard del C++ Non inserite una dichiarazione using in un header file
C++11 Lo specificatore di tipo decltype La classe string delle Librerie Standard del C++ A volte vogliamo definire alcuni oggetti dello stesso tipo di altri senza specificarlo esplicitamente Può essere molto utile quando il tipo è determinato dal risultato di un espressione (magari complicata) Il C++ permette di determinare il tipo durante la compilazione decltype(sqrt()) soluzione = delta; soluzione ha come tipo quello di ritorno della funzione sqrt decltype( (variabile) ); con doppie (( )) ritorna sempre una reference ad un tipo Una string è una sequenza variabile di caratteri È definita nel namespace std Per creare/manipolare oggetti della classe string è necessario scrivere using std::string; definizione e inizializzazione di una stringa: string s1; string s2 = s1; inizializzazione di default: s1 è una stringa vuota s2 è una copia di s1 decltype( variabile ); con singola ( ) ritorna una reference solo se variable è una reference string s3 = "Cpp"; string s4(10, S ); s3 è una copia di una stringa letterale s4 è "SSSSSSSSSS" Inizializzazione di stringhe Il C++ permette svariate forme di inizializzazione Inizializzando una variabile con l operatore =, chiediamo al compilatore una inizializzazione con copia dell oggetto senza l operatore =, si usa invece la inizializzazione diretta string s1; // Init default: string s2(s1); // Init diretta: stringa vuota string s2 = s1; // Init di copia, equiv. s2 copia di s1 a s2(s1) string s1("prova"); // Inizializzazione diretta string s1 = "Prova"; // Inizializzazione di copia Lettura e scrittura di stringhe Tramite la libreria iostream è possibile leggere e scrivere da terminale i tipi interni del linguaggio int, double,... Gli stessi operatori permettono di agire sulle stringhe std::cout << "Inserire una stringa: "; std::string s; std::cin >> s; std::cout << "Stringa: \"" << s << "\"" std::cin legge caratteri fino a quando non incontra un carattere whitespace compiliamo ed eseguiamo il programma g++ string1.cxx./a.out Inserire una stringa: Ciao String: "Ciao"./a.out Inserire una stringa: Ciao a tutti String: "Ciao"
Lettura di un numero variabile di stringhe Scriviamo un programma per leggere un numero sconoscuto di stringhe std::string word; while (std::cin >> word) std::cout << "Stringa: \"" << word << "\"" g++ -Wall -o string1 string1.cpp./string1./string1 Prima riga di input Stringa: "Prima" Stringa: "riga" Stringa: "di" Stringa: "input" Seconda Stringa: "Seconda" Il while controlla la validità della stream dopo ogni lettura. Se ci sono errori (per esempio End-Of-File), esce dal ciclo Lettura di una riga intera con le stringhe Scriviamo un programma per leggere un numero sconoscuto di righe (riga = caratteri fino a \n ) // Read input line until EOF while (std::getline(std::cin, line)) std::cout << "Line: \"" << line << "\"" g++ -Wall -o string2 string2.cpp./string2 Prova Line: "Prova" Uno Due Tre Line: "Uno Due Tre" getline() legge tutti i caratteri fino al carattere newline Operazioni sulle stringhe: empty() e size() La funzione empty() ritorna un bool che indica se la stringa è vuota while (std::getline(std::cin, line) ) if (!line.empty()) std::cout << line La funzione size() ritorna la lunghezza della stringa: il numero di caratteri in essa contenuti. while (std::getline(std::cin, line) ) if (!line.size() < 80) std::cout << line Il confronto tra stringhe La classe definisce numerosi operatori per il confronto di stringhe: confrontano i singoli caratteri (operazione "case-sensitive") gli operatori == e!= verificano l uguaglianza tra stringhe: due stringhe sono uguali se hanno la stessa lunghezza e contengono gli stessi caratteri gli operatori relazionali <, <=, >, >= verificano un ordinamento lessicografico tra stringhe (ordinamento dei dizionari) 1. date due stringhe di lunghezza diferente, se la più corta contiene è una sotto stringa di quella più lunga, la precede nell ordinamento 2. se due caratteri nella stessa posizione di due stringhe differiscono, il risultato del confronto nasce dall ordinamento del primo carattere differente tra le stringhe string s1 = "Hello"; string s2("hello World"); string s3 = "Hi"; s1 == s2 --> false s1 > s2 --> false s2 > s3 --> false s1!= s2 --> true s2 > s1 --> true s3 > s2 --> true
La concatenazione tra stringhe L operatore + permette di concatenare due stringhe (sum) L operatore composto += attacca l operando a destra alla stringa di sinistra (append) string s1 = "hello, "; string s2("world!"); string s3 = s1 + s2; s1 += s2; È possibile concatenare oggetti stringhe con stringhe costanti string s1 = "hello", s2("world"); string s3 = s1 + ", " + s2 + "!"; Mescolando stringhe e costanti uno dei due op. di tipo string string s4 = s1 + "!!!"; string s5 = "hello " + "Sky"; string s6 = s1 + ", " + "Sky"; string s7 = "hello" + ", " + s2; ok: hello!!! err: no string operand ok: (s1 + ",") + "Sky" err: can t add string literals Come accedere ai singoli caratteri di una stringa A volte è necessario accedere ai singoli caratteri della stringa L approccio migliore, per accedere ad ogni singolo carattere, è di usare una istruzione range for Sintassi: for (declaration : expression) statement; C++11 Una string rappresenta una sequenza di caratteri, pertanto possiamo usarla come expression nel range for std::string line("0123456789abcdef"); for (auto c : line) C++11 std::cout << c; auto dirà al compilatore di determinare il tipo di c, che nel nostro caso sarà char Esempio: contiamo i caratteri in una stringa Le funzioni cctype #include <cctype> std::string s; decltype(s.size()) alpha_cnt = 0; decltype(s.size()) digit_cnt = 0; decltype(s.size()) other_cnt = 0; std::cout << "Letters: " << alpha_cnt << " Numbers: " << digit_cnt << " Others: " << other_cnt isalnum(c) isalpha(c) isdigit(c) iscntrl(c) ispunct(c) islower(c) true se c è lettera o numero true se c è una lettera true se c è una cifra numerica true se c è un carattere di controllo true se c è un carattere di interpunzione true se c è una lettera minuscola std::getline(std::cin, s); for (auto c : s) if (isalpha(c)) ++alpha_cnt; else if (isdigit(c)) ++digit_cnt; else ++other_cnt; g++ -W -Wall -std=c++0x str_cnt.cxx./a.out Test del programma 12345 &^#% Letters: 16 Numbers: 5 Others: 8 isupper(c) isspace(c) tolower(c) toupper(c) true se c è una lettera maiuscola true se c è un whitespace: (spazio, tab, vertical tab, return, newline o formfeed) ritorna il carattere minuscolo corrispondente ritorna il carattere maiuscolo corrispondente
Accesso ai singoli caratteri di una stringa A volte è necessario accedere soltanto ad alcuni caratteri della stringa (per esempio trasformare la prima lettera in maiuscola) Ci sono due possibilità: 1. utilizzare l operatore [] 2. usare un iteratore L operatore [] prende un string::size_type come indice dalla posizione del carattere e ritorna una reference al carattere indicato NB indici da 0 (primo carattere) a string::size() - 1 (ultimo carattere) if (!s.empty()) std::cout << s std::cout << s[0] s[0] = std::toupper(s[0]); std::cout << s hello World! h Hello World! Iterare su una stringa Come altro esempio, iteriamo sui caratteri di una stringa e modifichiamo la prima parola for (decltype(s.size()) index = 0; index!= s.size() &&!isspace(s[index]); ++index) s[index] = toupper(s[index]); cout << s << endl; Usiamo la variabile index per accedere ai singoli caratteri Il tipo viene definito correttamente con decltype() il ciclo procede fino a che non si raggiunge la fine della stringa AND (operatore &&) non si incontra uno spazio L indice di una stringa non viene automaticamente verificato: controllare che sia >=0 e sempre < size() della stringa HELLO World! se l indice è di tipo string::size_type non può essere negativo Esempio: accesso casuale ad una stringa Si vogliono convertire numeri dalla base 10 alla base 16 const string hexdigits = "0123456789ABCDEF"; cout << "Inserire una sequenza di numeri tra 0 e 15" << separati da spazi. Inserire <RETURN> per terminare" << endl; string result; string::size_type n; while (cin>> n) // Per contenere in numero convertito // Il numero da convertire if (n < hexdigit.size()) result += hexdigits[n]; cout << "Il numero esadecimale e : " << result << end; 12 0 5 15 8 15 Il numero esadecimale è: C05F8F os << s is << s Sommario: operazioni sulle stringhe getline(is, s) s.empty() Invia s sulla stream di output os. Ritorna os. Legge stringhe separati da caratteri "whitespace" da is e li immagazzina in s. Ritorna is. Legge una linea completa di input da is e la immagazzina in s. Ritorna is. Ritorna true se s è vuota, false altrimenti. s.size() Ritorna il numero di caratteri contenuti in s. s[k] Ritorna una reference al carattere in posizione k. s1+ s2 Ritorna una stringa che è il risultato della concatenazione di s1 e s2. s1 = s2 Rimpiazza s1 con una copia di s2. s1 == s2 Ritorna true se le stringhe sono uguali. s1!= s2 Ritorna true se le stringhe sono diverse. <, <=, >, >= operatori per il confronto tra stringhe. I confronti sono case-sensitive e l ordinamento è lessicografico (propria dei dizionari).