Nu obții 100 de puncte sau ai nelămuriri în privința problemelor? Scrie-mi pe Instagram.
Ai găsit o greșeală, vrei să raportezi un utilizator sau vrei să comunici altceva? Folosește formularul de contact.
Vrei să ne transmiți o părere despre platformă? Folosește formularul de feedback.
Folosește următoarele shortcuturi pentru a naviga mai ușor pe platformă.
Meniu shortcuturi | ? |
Căutare probleme sau utilizatori | / |
Navigare printre rezultatele căutării | ↑, ↓ |
Meniu de contact și feedback | CTRL + Shift + F |
Ieșire din meniuri | Esc |
Setări editor | CTRL + Shift + S |
Schimbare stil editor | CTRL + Shift + E |
Șabloane de cod | CTRL + Shift + 1/2/3 |
Golire editor | CTRL + Shift + 4 |
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.
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
).
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.
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
"
) ș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.
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.
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 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.
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:
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;
}
Ș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:
0
la lungime - 1
;for
, cu condiția de oprire s[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] << " ";
}
Î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
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
așteaptă un caracter c
și returnează:
1
, dacă c
este cifră (de la 0
la 9
);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
așteaptă un caracter c
și returnează:
1
, dacă c
este literă a alfabetului englez (mică sau mare) sau cifră (de la 0
la 9
);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
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
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
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
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
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
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
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
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
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
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
așteaptă un șir de caractere s
și un caracter c
și returnează:
NULL
(adică 0
), dacă caracterul c
nu se găsește deloc în s
;c
în s
, 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
așteaptă doi parametri șiruri de caractere s
și t
și returnează:
NULL
(adică 0
), dacă șirul t
nu se află deloc în șirul s
;t
în s
, 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
așteaptă două șiruri de caractere s
și t
și returnează:
-1
), dacă s
este lexicografic mai mic decât t
;0
, dacă cele două șiruri sunt identice;1
), dacă s
este lexicografic mai mare decât t
.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
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.
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
Iată câteva greșeli frecvente care apar în lucrul cu șirurile de caractere.
strcpy
, nu cu egalitatePoate 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!
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
Iată câteva aplicații, împreună cu rezolvări.
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;
}
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;
}
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;
}
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 + ???;
# | Problemă | Dificultate | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
177. | Uppercase | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
320. | Interschimbare litere | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
325. | Zibelina | Medie (4 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
335. | Parantezare maxima | Medie (4 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
289. | Frigider | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vrei mai multe probleme? Pe această pagină găsești întreaga listă de probleme propuse. |
# | Problemă | Dificultate | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
342. | Extragere numere | Medie (4 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
362. | Propozitie de lungime maxima | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
405. | Palindrom dupa concatenare | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
366. | Cuvinte de lungime para | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
397. | Grila 2 | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vrei mai multe probleme? Pe această pagină găsești întreaga listă de probleme propuse. |
# | Problemă | Dificultate | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
419. | Decriptare Atbash | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
420. | Cifrul Cezar 3 | Medie (4 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
410. | Cifrul Cezar | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
417. | Decriptare A1Z26 | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
415. | Pasareste 2 | Ușoară (2 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vrei mai multe probleme? Pe această pagină găsești întreaga listă de probleme propuse. |