Șiruri de caractere în C++. Tot ce trebuie să știi
Limbajul C++ permite prelucrarea unor bucăți de text prin intermediul așa
numitelor șiruri de caractere. Șirurile de caractere sunt defapt niște
vectori de elemente char
și există o sumedenie de funcții ajutătoare care
permit prelucrarea lor cu ușurință, despre care vom vorbi în această lecție.
Cum se memorează un șir de caractere
Un șir de caractere se reține ca un vector de elemente char
. Spre deosebire
de vectorii de numere care puteau începe și de pe poziția 1
, șirurile de
caractere încep întotdeauna de pe poziția 0
. După ultimul caracter se pune
un caracter NULL
('\0'
), care marchează sfârșitul șirului (codul
ASCII al acestui caracter este 0
).
Astfel, de pildă, reprezentarea cuvântului liceu
în memorie într-un șir de
caractere s
se face în felul următor:
i |
0 |
1 |
2 |
3 |
4 |
5 |
---|---|---|---|---|---|---|
s[i] |
l |
i |
c |
e |
u |
\0 |
Vedem că acest cuvânt are lungimea 5
, cu caracterele aflate pe pozițiile de
la 0
la 4
, iar sfârșitul cuvântului, este marcat prin caracterul '\0'
de
la final (pe poziția 5
).
Cum se declară un șir de caractere
Un șir de caractere se declară ca un vector de elemente char
:
char s[101];
De obicei, șirurile de caractere se notează cu s
, însă orice altă denumire
este perfect validă. Avem 101
caractere cu indici între 0
și 100
. Cum la
finalul oricărui șir de caractere trebuie să se afle un '\0'
, putem folosi
100
de caractere dintre cele 101
pentru șirul nostru de caractere.
Valoare inițială pentru șir de caractere
Unui șir de caractere i se poate atribui o valoare chiar la declarare. Iată
câteva moduri de a reține șirul InfoAs
:
char a[11] = "InfoAs"; //primele 6 caractere sunt I, n, f, o, A, s, iar de acolo toate sunt caractere '\0'
char b[] = "InfoAs"; //lungimea lui b este 7, pentru a reține doar caracterele I, n, f, o, A, s, '\0'
char c[11] = {'I', 'n', 'f', 'o', 'A', 's', '\0'}; //similar ca la vectori cu int, putem face așa
char d[] = {'I', 'n', 'f', 'o', 'A', 's', '\0'}; //sau așa
Când se pun ghilimele ("
) și când se pune apostrof ('
)?
Ghilimele duble ("
) se pun în momentul în care vrem să lucrăm cu un întreg
șir de caractere, de pildă când atribuim la declarare o valoare inițială:
char s[11] = "InfoAs";
În schimb, când lucrăm cu caractere individuale,
punem un singur apostrof, '
.
În exemplele de mai jos, acest lucru o să devină mai evident.
Ce caractere putem include în șirurile de caractere?
Pentru simplitate, limbajul C++ folosește doar caracterele din tabelul
ASCII. Sunt doar 128
(sau 256
) de
caractere, însă în general sunt cam toate caracterele necesare pentru
problemele de informatică. Printre acestea se numără: caracterele cifră,
litere mici ale alfabetului englez, litere mari ale alfabetului englez, câteva
simboluri simple (!?.,:;()[]{} @#$%^&*-_=+
și altele) și câteva alte
caractere.
Nu există caractere mai complexe precum emojiuri sau litere cu diacritice, însă în cazul problemelor de informatică, acest fapt nu reprezintă un impediment. Lecția cu tabelul ASCII prezintă toate caracterele într-un mod ușor de înțeles.
Afișarea unui șir de caractere
Deși sunt vectori în esență, șirurile de caractere se pot afișa direct, ca o variabilă obișnuită:
char s[] = "InfoAs";
cout << s << endl;
Afișarea se face automat. Se afișează caracterele începând de pe poziția 0
,
însă afișarea se oprește când se ajunge la primul caracter '\0'
.
Citirea unui șir de caractere
Citirea se face, asemănător, ca la o variabilă obișnuită:
char s[101];
cin >> s;
Citirea se face până la întâlnirea primului spațiu sau până la primul enter. Prin urmare, citirea de acest tip nu va putea citi propoziții.
Citirea propozițiilor cu șiruri de caractere
Pentru a citi șiruri care conțin spații (precum propozițiile), se poate folosi
funcția cin.getline()
, care arată în felul următor:
char s[101];
cin.getline(s, 101); //șirul și lungimea sa
Se așteaptă doi parametri:
- un șir de caractere în care să se citească;
- lungimea maximă de citire (se recomandă punerea lungimii maxime a șirului, adică numărul din declarare).
Atenție! Funcția cin.getline()
citește până la întâlnirea unui caracter
enter ('\n'
, numit și newline
sau endline
) sau până la finalul citirii.
Cu toate acestea, dacă după citire apare un '\n'
, se va sări peste acesta în
continuarea citirii, însă nu se va include în șirul citit.
Iată un exemplu de citire cu cin.getline()
:
#include <iostream>
using namespace std;
int main()
{
char nume[51], judet[51];
cout << "Numele tau este: ";
cin.getline(nume, 51); //introdu numele tău complet, cu spații
cout << "Esti din judetul: ";
cin.getline(judet, 51);
cout << "Salut, " << nume << "! Esti din judetul " << judet << ".";
return 0;
}
Parcurgerea unui șir de caractere. Extragerea unui caracter
Șirurile de caractere sunt vectori de elemente char
. Din acest motiv, putem
să extragem caracterele pe rând exact ca la un vector de numere:
char s[] = "spanac"; //începând de pe poziția 0: s, p, a, n, a, c
cout << s[1]; //afișare: p
s[3] = 's'; //șirul devine "spasac"
cout << s[4]; //afișare: a
cout << s[3]; //afișare: s
//Cod greșit sau impredictibil:
cout << s[-1];
cout << s[9999999];
Parcurgerea unui șir de caractere se poate realiza în două moduri:
- dacă știm lungimea șirului (mai jos se prezintă o astfel de metodă), putem parcurge caracterele de la
0
lalungime - 1
; - dacă nu știm lungimea, putem parcurge folosind o structură repetitivă
for
, cu condiția de oprires[i] != '\0'
(adică nu ne aflăm la finalul șirului).
Iată parcurgerea unui șir de caractere și afișarea caracterelor cu spații între ele:
char s[101];
cin >> s;
for(int i = 0; s[i] != '\0'; i++) {
cout << s[i] << " ";
}
Funcții predefinite cu șiruri de caractere
În limbajul C++ există multe funcții scrise de dinainte, care permit lucrul mai ușor cu caractere. Acestea pot părea complicate la început, însă au logică și ușurează destul de mult munca cu șiruri de caractere.
Toate funcțiile de mai jos se regăsesc în biblioteca <cstring>
. Prin urmare,
la începutul programului, sub includerea bibliotecii <iostream>
, trebuie
scris următorul rând:
#include <cstring>
Funcția isalpha — cum funcționează
Funcția isalpha
așteaptă un caracter c
și returnează:
-
1
, dacăc
este literă (mică sau mare) din alfabetul englez; -
0
, în caz contrar.char c; cin >> c; if(isalpha(c)) { cout << "Am citit o litera"; } else { cout << "Nu am citit o litera"; }
Funcția isdigit — cum funcționează
Funcția isdigit
așteaptă un caracter c
și returnează:
-
1
, dacăc
este cifră (de la0
la9
); -
0
, în caz contrar.char c; cin >> c; if(isdigit(c)) { cout << "Am citit o cifra"; } else { cout << "Nu am citit o cifra"; }
Funcția isalnum — cum funcționează
Funcția isalnum
așteaptă un caracter c
și returnează:
-
1
, dacăc
este literă a alfabetului englez (mică sau mare) sau cifră (de la0
la9
); -
0
, în caz contrar.
Practic, funcția isalnum(c)
este echivalentă cu expresia is isalpha(c) || isdigit(c)
.
Mai jos este prezentată o funcție care validează un nume de utilizator, care poate conține doar litere și cifre:
int este_nume_utilizator_valid(char numeUtilizator[]) {
for(int i = 0; numeUtilizator[i] != '\0'; i++) {
if(isalnum(numeUtilizator[i]) == 0) {
return 0;
}
}
return 1;
}
Funcția islower — cum funcționează
Funcția islower
așteaptă un caracter c
și returnează:
-
1
, dacăc
este o literă mică; -
0
în caz contrar (nu este literă sau este literă mare).islower('a'); //1 islower('B'); //0 islower('!'); //0
Funcția isupper — cum funcționează
Funcția isupper
așteaptă un caracter c
și returnează:
-
1
, dacăc
este o literă mare; -
0
în caz contrar (nu este literă sau este literă mică).isupper('a'); //0 isupper('B'); //1 isupper('!'); //0
Funcția tolower — cum funcționează
Funcția tolower
așteaptă un caracter c
și returnează:
-
c
, dacăc
este o literă mică sau nu este literă; -
c
ca literă mică, dacă este o majusculă.tolower('a'); //a tolower('B'); //b tolower('!'); //!
Funcția toupper — cum funcționează
Funcția toupper
așteaptă un caracter c
și returnează:
-
c
, dacăc
este o literă mare sau nu este literă; -
c
ca literă mare, dacă este o literă mică.toupper('a'); //A toupper('B'); //B toupper('!'); //!
Funcția strlen — cum funcționează
Funcția strlen
așteaptă un șir de caractere s
și returnează lungimea
acestuia. Caracterul '\0'
este exclus din numărare.
char s[] = "InfoAs";
cout << strlen(s) << endl; //Afișare: 6
cout << strlen(s + 3) << endl; //Șirul este "oAs" | Afișare: 3
Funcția strcpy — cum funcționează
Funcția strcpy
așteaptă două șiruri de caractere dest
și src
, după care
copiază conținutul lui src
în dest
(ștergând ce era înainte în dest
).
Atenție! Funcția strcpy
se folosește uneori în ștergerea sau inserarea
caracterelor, unde dest
și src
au părți care se suprapun. Se recomandă
folosirea unui șir auxiliar în astfel de situații, pentru că dacă dest
și
src
se suprapun, comportamentul este deseori impredictibil.
char s[101], t[101];
strcpy(s, "Info");
cout << s << endl; //Afișare: Info
strcpy(t, s);
cout << t << endl; //Afișare: Info
strcpy(s, "As");
strcpy(t + 4, s);
cout << t << endl; //Afișare: InfoAs
Eliminarea unui caracter dintr-un șir cu strcpy
Pentru a șterge caracterul de pe poziția poz
dintr-un șir s
, ne folosim de
un șir auxiliar t
:
char s[101], t[101];
int poz;
cin >> s >> poz;
strcpy(t, s + poz + 1); //copiem în t tot ce este după poz
strcpy(s + poz, t); //punem pe poziția poz ce era în t, adică eliminăm caracterul de pe poziția poz
Inserarea unui caracter într-un șir cu strcpy
Pentru a insera un caracter c
pe poziția poz
a unui șir s
, ne folosim de
un șir auxilar t
:
char s[101], t[101], c;
int poz;
cin >> s >> poz >> c;
strcpy(t, s + poz); //copiem în t tot ce este după caracterul ce vrem să îl inserăm
s[poz] = c; //punem c pe poziția curentă
strcpy(s + poz + 1, t); //după caracterul pus c punem ce este în t, adică tot ce aveam după
Funcția strncpy — cum funcționează
Funcția strncpy
așteaptă două șiruri de caractere dest
și src
, împreună
cu un număr cnt
și copiază în dest
primele cnt
caractere din src
(sau
șirul întreg src
dacă cnt
este mai mare decât lungimea acestuia). După
cele cnt
caractere, nu se plasează caracterul '\0'
și trebuie plasat
manual pentru a evita erorile.
char s[101] = "salut";
strncpy(s + 3, "ariu----", 4);
cout << s << endl; //Afișare: salariu
Atenție! Șirurile dest
și src
nu trebuie să se suprapună, similar ca
la funcția strcpy
.
Funcția strcat — cum funcționează
Funcția strcat
așteaptă două șiruri de caractere dest
și src
și
concatenează (lipește) la finalul șirului dest
, șirul src
.
char s[101] = "pis", t[101] = "ica";
strcat(s, t);
cout << s << endl; //Afișare: pisica
strcat(t, s + 1);
cout << t << endl; //Afișare: icaisica
Atenție! Ca și la funcția strcpy
, la funcția strcat
, șirurile dest
și src
nu se pot suprapune.
Funcția strchr — cum funcționează
Funcția strchr
așteaptă un șir de caractere s
și un caracter c
și
returnează:
-
NULL
(adică0
), dacă caracterulc
nu se găsește deloc îns
; -
adresa primei apariții a lui
c
îns
, dacă există.//Poți deduce ce face programul acesta? if(strchr("aeiou", c) != NULL) { cout << "V"; } else { cout << "C"; }
char alfabet[] = "abcdefghijklmnopqrstuvwxyz", c; cin >> c; //literă mică din aflabetul englez cout << "Litera " << c << " se află pe poziția " << (strchr(alfabet, c) - alfabet + 1) << " din alfabet.";
Funcția strstr — cum funcționează
Funcția strstr
așteaptă doi parametri șiruri de caractere s
și t
și
returnează:
-
NULL
(adică0
), dacă șirult
nu se află deloc în șiruls
; -
adresa prime apariții a lui
t
îns
, dacă există.char s[] = "abcabc", t[] = "bc"; cout << strstr(s, t); //Afișare: bcabc, pentru că strstr(s, t) este pointer către prima apariție de "bc" din "abcabc" (poziția 1)
Funcția strcmp — cum funcționează
Funcția strcmp
așteaptă două șiruri de caractere s
și t
și returnează:
-
Un număr negativ (nu neapărat
-1
), dacăs
este lexicografic mai mic decâtt
; -
0
, dacă cele două șiruri sunt identice; -
Un număr pozitiv (nu neapărat
1
), dacăs
este lexicografic mai mare decâtt
.char s[] = "ardei", t[] = "andrei"; if(strcmp(s, t) > 0) { cout << "s mai mare"; //aici intră, pentru că ardei > andrei } else { cout << "t mai mare"; }
Funcția strtok — cum funcționează
Funcția strtok
este folosită pentru a extrage dintr-un șir de caractere mai
multe subșiruri (de pildă, cuvinte), separate prin mai multe caractere
diferite. Cel mai simplu mod de a vedea cum funcționează funcția este de a
urmări cel mai general exemplu:
char s[] = "Ce fain este... InfoAs!! Asa,i?";
char sep[] = "!?., "; //caracterele ce separă cuvintele
char * p = strtok(s, sep); //salvăm într-un pointer subșirul (cuvântul) curent. la prima apelare a funcției strtok, dăm șirul s și un șir de caractere ce delimitează cuvintele
while(p != NULL) { //cât timp avem cuvinte
cout << p << "\n"; //prelucrăm cuvintele; în acest caz, afișăm pe ecran
p = strtok(NULL, sep); //de data aceasta, în loc să dăm ca parametru s, dăm NULL, pentru a continua căutarea următorului cuvânt de unde am rămas
}
Programul de mai sus afișează:
Ce
fain
este
InfoAs
Asa
i
Mai exact, strtok
se așteaptă prima oară la șirul nostru și la caracterele
de separare, după care, de la următoarele apelări, trebuie să primească NULL
pentru a continua de unde a rămas. Atunci când funcția strtok
returnează
NULL
, înseamnă că am ajuns la punctul de oprire.
Atenție! Șirul inițial s
se modifică în urma apelării funcției strtok
.
Prin urmare, pentru a păstra șirul original, trebuie făcută o copie.
Pointeri în C++. Tipul char *
(char steluță)
Pointerii se discută mai în detaliu în această lecție.
Să declarăm un șir de caractere:
char s[] = "InfoAs";
Am discutat că s
este un tablou unidimensional — un șir de caractere,
reprezentat printr-un vector de char
. Însă, în esență, s
este defapt un
pointer, adică atunci când îl apelăm pe s
în programul nostru, tot ce
facem este să mergem pe poziția unde este salvată informația din s
, adică
"InfoAs"
în cazul nostru. Mai precis de atât, s
este un pointer către
primul caracter din șirul nostru, adică către caracterul 'I'
.
Tipul char *
este o metodă de a reține un pointer, însă fără să avem un loc
definit unde să salvăm informația (spre deosebire de s
, unde am făcut loc
pentru a reține "InfoAs"
).
Un pointer se declară ca mai jos și i se poate atribui o anumită adresă de
memorie (spre exemplu, locația de unde începe s
), lucru ce îi dă
posibilitatea să funcționeze ca șirul de caractere căruia i s-a dat adresa:
char s[] = "InfoAs";
//Declarare pointer p:
char *p;
//p în acest moment reține o locație aleatorie, exact ca o variabilă neinițializată, deci nu putem face nimic cu el încă
cout << s << "\n"; //afișare: InfoAs
p = s; //p este acum echivalent cu s
cout << p << "\n"; //afișare: InfoAs
cout << p[3] << "\n"; //afișare: o
p = p + 1;
cout << p << "\n"; //afișare: nfoAs
p = s + 4;
cout << p << "\n"; //afișare: As
Greșeli frecvente
Iată câteva greșeli frecvente care apar în lucrul cu șirurile de caractere.
Atribuirea se face cu strcpy
, nu cu egalitate
Poate fi tentat să atribuim cu operatorul de egalitate, ca mai jos. Însă, șirurile de caractere sunt defapt vectori în esență, iar doi vectori nu se pot egala în acest fel:
char s[101], t[101] = "abc";
//Greșit: s = t;
strcpy(s, t); //Corect!
Depășirea limitei de memorie
Se poate întâmpla să depășim limita de memorie, în special dacă nu suntem atenți la lungimile șirurilor:
char s[5], alfabet[] = "abcdefghijklmnopqrstuvwxyz";
strcpy(s, alfabet); //Greșit: s nu poate reține atâtea caractere
Probleme rezolvate
Iată câteva aplicații, împreună cu rezolvări.
Este caracterul o vocală
Dându-se un caracter c
literă mică din alfabetul englez, să se determine
dacă este sau nu o vocală.
Exemple: Pentru c = 'a'
, răspunsul este DA
, dar pentru c = 'z'
,
răspunsul este NU
.
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char c;
cin >> c;
if(strchr("aeiou", c) == 0) {
cout << "NU";
} else {
cout << "DA";
}
return 0;
}
Uppercase (transformarea unui șir cu litere mari)
Dându-se un șir de caractere s
, să se transforme toate literele sale în
litere mari. Testează-ți rezolvarea pe această pagină.
Exemplu: Pentru s = "sA luT"
, rezultatul va fi s = "SA LUT"
.
Rezolvare: Iterăm printre caracterele lui s
folosind o structură
repetitivă de tip for
. Pentru fiecare caracter, verificăm dacă este literă
și dacă este mică folosind funcțiile predefinite isalpha()
și islower()
,
după care îi actualizăm valoarea dacă este cazul folosind funcția toupper()
.
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s[101];
cin.getline(s, 101);
for(int i = 0; s[i] != '\0'; i++) {
if(isalpha(s[i]) && islower(s[i])) {
s[i] = toupper(s[i]);
}
}
cout << s;
return 0;
}
Separare cuvinte
Dându-se un șir de caractere s
format din litere mici ale alfabetului
englez, împreună cu simbolurile .,:;!?
și spațiu, să se despartă șirul în
cuvinte. Testează-ți rezolvarea pe această pagină.
Exemplu: Pentru s = "salut,, ce mai.faci...??!"
, avem cuvintele salut
,
ce
, mai
și faci
.
__** Rezolvare:__** Folosim funcția
predefinită strtok
. Avem drept caractere de separare cele menționate mai
sus. Afișăm pe rând fiecare cuvânt din while
. Codul arată în felul următor:
#include <iostream>
#include <cstring>
using namespace std;
char s[256];
int main()
{
cin.getline(s, 256);
char *p = strtok(s, " .,:;!?");
while(p != NULL) {
cout << p << "\n";
p = strtok(NULL, " .,:;!?");
}
return 0;
}
Exerciții propuse
Completează următoarele secvențe de cod:
Să se despartă șirul în cuvinte după caracterul .
(punct):
char s[256];
cin.getline(s, 256);
char *p = strtok(s, ???);
while(p != NULL) {
p = strtokk(NULL, ???);
}
Să se verifice dacă un caracter c
este cifră sau literă mică:
char c;
cin >> c;
if(???(c) || islower(???)) {
cout << "DA";
}
Să se afișeze ultima literă a șirului s
:
char s[] = "abcdef";
cout << s + ???;
Probleme propuse
Probleme de prelucrare a șirurilor de caractere
Setul de probleme 251 nu a fost găsit.
Probleme cu funcții predefinite
Setul de probleme 252 nu a fost găsit.
Probleme de criptografie
Setul de probleme 253 nu a fost găsit.
Alte resurse și bibliografie
DS
Autorul acestei lecții
Dominic Satnoianu
Această lecție a fost redactată de către Dominic Satnoianu.
© 2021 – 2025 Aspire Education Labs SRL. Toate drepturile rezervate.
Așa cum este specificat și în termeni și condiții, conținutul acestei pagini este protejat de legea drepturilor de autor și este interzisă copierea sau modificarea acestuia fără acordul scris al autorilor.
Încălcarea drepturilor de autor este o infracțiune și se pedepsește conform legii.
Comentarii 0