Ș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 la lungime - 1;
  • dacă nu știm lungimea, putem parcurge folosind o structură repetitivă 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] << " ";
}

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 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 — 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 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 — 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ă caracterul c nu se găsește deloc în s;

  • adresa primei apariții a lui 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 — cum funcționează

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;

  • adresa prime apariții a lui 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 — 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ât t;

  • 0, dacă cele două șiruri sunt identice;

  • Un număr pozitiv (nu neapărat 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 — 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

Autentifică-te pentru a putea comenta.

Autentifică-te