2.4 Le stringhe di caratteri (C e C++) — Versione approfondita
Obiettivi di apprendimento
- Capire la differenza tra char[] (C) e std::string (C++).
- Gestire input con spazi in modo sicuro (
cinvsgetline,ignore(),ws). - Applicare i metodi chiave:
size(),substr(),find(),replace(),erase(),at(). - Convertire tra C-string e
std::string, usarestoi/to_string, evitare errori tipici.
Stringhe in C (array di char)
In C una stringa è un array di caratteri terminato dal carattere speciale '\0' (terminatore). Senza di esso le funzioni C non sanno dove la stringa termina.
char ok[6] = {'r','o','s','s','o','\0'}; // CORRETTA
char err[5] = {'r','o','s','s','o'}; // NO: manca '\0'
char s[] = "rosso"; // il compilatore aggiunge '\0' (dimensione 6)
Dimostrazione con sizeof e stampa caratteri
#include <iostream>
using namespace std;
int main(){
char colore[] = "rosso";
cout << "byte: " << sizeof(colore) << '\n';
for(size_t i=0;i<sizeof(colore);++i)
cout << "colore[" << i << "] = " << colore[i] << '\n';
}
Overflow & input C-style. Funzioni come
gets/strcpy/strcat non controllano la capienza del buffer. Evita gets; se ti servono versioni C usa fgets/strncpy/strncat con dimensioni corrette.
Input sicuro in C++: cin vs getline
cin >> s si ferma al primo spazio. Per leggere frasi utilizza getline(cin, s). Se li combini, “pulisci” lo stream dal newline residuo con cin.ignore() (o cin >> ws).
#include <iostream>
#include <string>
#include <limits> // per numeric_limits
using namespace std;
string nome, indirizzo;
cin >> nome; // legge solo la prima parola
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // pulisci newline
getline(cin, indirizzo); // ora legge l'intera riga con spazi
Esempio completo con getline
#include <iostream>
#include <string>
using namespace std;
int main(){
string intestazione = "Riga di intestazione";
string nome, indirizzo;
cout << intestazione << '\n';
cout << "Nome e cognome: "; getline(cin, nome);
cout << "Indirizzo: "; getline(cin, indirizzo);
cout << nome + ", " + indirizzo << '\n';
}
std::string (C++) — costruttori, accesso, capacità, modifiche
Perché std::string? Gestisce memoria e terminatore automaticamente, offre operatori e metodi per confrontare, cercare, inserire, sostituire e cancellare testo in modo sicuro.
Costruttori utili
string a; // vuota
string b = "testo"; // da literal
string c("testo"); // equivalente
string d(5, '*'); // "*****"
string e(b, 0, 3); // "tes"
string f = b.substr(2, 2); // "st"
Accesso ai caratteri
string s = "Lambda";
char x = s[0]; // 'L' (nessun controllo)
char y = s.at(1); // 'a' (controllo limiti: può lanciare out_of_range)
s.front() = 'l'; // "lambda"
s.back() = '!'; // "lambda!"
Capacità & gestione memoria
string s = "ciao";
s.size(); // 4
s.empty(); // false
s.reserve(100); // pre-alloca (utile con molte append)
s.shrink_to_fit(); // prova a ridurre la capacità
Modificatori principali
string s = "Buona giornata!";
s.append(" :)");
s.push_back('!');
s.insert(6, "bellissima ");
s.replace(0, 5, "Ottima");
s.erase(6, 3);
s.clear();
Sottostringhe e ricerca
string s = "Buona giornata!";
auto p1 = s.find("na"); // 2
auto p2 = s.rfind("na"); // ultima occorrenza
auto p3 = s.find_first_of("aeiou"); // prima vocale
auto p4 = s.find_first_not_of(" "); // primo non-spazio
if (s.find("xyz") == string::npos) { /* non trovato */ }
Tutte le occorrenze di una sottostringa
string s = "banana bandana";
string sub = "ana";
for (size_t pos = s.find(sub); pos != string::npos; pos = s.find(sub, pos + 1)){
cout << "Trovata in pos " << pos << '\n';
}
Confronto e conversioni
// confronto
string a="alfa", b="beta";
bool uguali = (a == b); // false
int cmp = a.compare(b); // <0 se a<b (lessicografico)
// conversioni numeriche
int n = stoi("123"); // 123
double x = stod("3.14"); // 3.14
string t = to_string(42); // "42"
// interfaccia C
const char* cstr = a.c_str(); // per funzioni C
Eccezioni comuni:
at() può lanciare out_of_range; alcune operazioni possono lanciare length_error se superi la capacità massima.Unicode/UTF-8: la lunghezza in caratteri può differire dai byte;
size() conta i byte. Per elaborazioni Unicode avanzate valuta librerie dedicate.
C (char[]) vs C++ (std::string)
| Aspetto | char[] (C) | std::string (C++) |
|---|---|---|
| Terminatore | Richiesto '\0' |
Gestito internamente |
| Lunghezza | Fissa alla dichiarazione | Dinamica |
| Sicurezza | Rischio overflow | at() controlla i limiti |
| Operazioni | strcpy/strcat/strcmp |
append/replace/find/compare |
| Interop C | Nativa | c_str() → const char* |
Ricette veloci
Trim spazi iniziali/finali
auto ltrim = [](string &s){ s.erase(0, s.find_first_not_of(' ')); };
auto rtrim = [](string &s){ s.erase(s.find_last_not_of(' ')+1); };
auto trim = [&](string &s){ ltrim(s); rtrim(s); };
Split su un delimitatore
vector<string> split(const string &s, char sep){
vector<string> out; string cur;
for(char ch: s){
if(ch==sep){ out.push_back(cur); cur.clear(); }
else cur.push_back(ch);
}
out.push_back(cur);
return out;
}
Esercizi (con soluzioni a scomparsa)
- Dato
string s="Programmare in C++";stampa gli indici di tutte le occorrenze di"ra".
Soluzione
for(size_t p = s.find("ra"); p != string::npos; p = s.find("ra", p+1)) cout << p << ' '; - Con
string a="alfa"; string b="beta";usacompare()per dire quale viene prima.
Soluzione
int cmp = a.compare(b); cout << (cmp<0 ? "alfa prima di beta" : (cmp>0 ? "beta prima di alfa" : "uguali")); - Dalla stringa
"Arrivederci"ottieni"veder"senza cicli.
Soluzione
string s="Arrivederci"; string out = s.substr(3,5); - Trasforma
"Buona giornata!"in"Ottima giorna?"con due operazioni.
Soluzione
string s="Buona giornata!"; s.replace(0,5,"Ottima"); s.erase(6,3); s.back()='?'; - Converte
"-120"in intero e poi di nuovo in stringa con suffisso"px".
Soluzione
int n = stoi("-120"); string t = to_string(n) + "px";
Errori comuni (checklist)
- Dimenticare
'\0'nelle char[]. - Mescolare
cin >>egetlinesenza pulire lo stream (ignoreo>> ws). - Usare
[]fuori range: preferisciat()quando l’indice non è garantito valido. - Confondere byte e caratteri con UTF-8.