Șirurile de caractere apar la BAC informatică în aproape fiecare sesiune: la itemii scurți din Subiectul II („ce se afișează în urma executării secvenței…”) și la programul din Subiectul III. În C++ lucrăm la BAC cu șiruri în stilul C: tablouri de tip char terminate cu caracterul nul '\0', prelucrate cu funcțiile din biblioteca <cstring>. Dacă înțelegi cum arată un astfel de șir în memorie, toate funcțiile devin logice.

Cum arată un șir char[] în memorie

Declarația char s[21] = "bac"; rezervă 21 de octeți. În primii patru se află 'b', 'a', 'c' și '\0'. Terminatorul nul marchează sfârșitul șirului — toate funcțiile din <cstring> se opresc la el. De aici rezultă prima regulă: un șir de cel mult 20 de caractere are nevoie de un tablou de cel puțin 21.

Fiecare caracter este, de fapt, un cod numeric (codul ASCII). Merită să reții câteva valori: '0' = 48, 'A' = 65, 'a' = 97. Literele mari și cele mici corespunzătoare diferă prin 32, iar literele sunt consecutive în alfabet. Consecințe utile:

Citirea unui șir de caractere

cin >> s citește doar până la primul spațiu, deci e potrivit doar pentru un singur cuvânt. Pentru un text care conține spații, folosește cin.getline(s, 101), unde 101 este numărul maxim de caractere citite plus unu (pentru terminator). Dacă înainte ai citit un număr cu cin >> n, în buffer rămâne caracterul de linie nouă; scapă de el cu cin.get() înainte de getline, altfel vei citi un șir vid.

Funcțiile de bază din cstring

FuncțieCe faceExemplu
strlen(s)numărul de caractere până la '\0'strlen("informatica") = 11
strcpy(d, s)copiază s în d (inclusiv terminatorul)d devine o copie a lui s
strcat(d, s)adaugă s la finalul lui d"bac" + "info" → "bacinfo"
strcmp(a, b)compară lexicografic<0, 0 sau >0
strchr(s, c)adresa primei apariții a caracterului cNULL dacă nu apare
strstr(s, t)adresa primei apariții a subșirului tNULL dacă nu apare

strlen

Returnează lungimea, fără terminator. Pentru char s[] = "informatica";, strlen(s) este 11, iar caracterele sunt la pozițiile 0…10. Parcurgerea standard este for (i = 0; i < n; i++), cu n = strlen(s) calculat o singură dată, înainte de buclă.

strcpy și strcat

Ambele scriu în primul parametru, așa că acesta trebuie să aibă loc suficient. Exemplu:

char a[51] = "bac", b[51] = "info";
strcat(a, " ");
strcat(a, b);
cout << a;          // bac info
strcpy(b, a + 4);
cout << b;          // info

Expresia a + 4 înseamnă „șirul care începe de la poziția 4 a lui a”. Asta e o idee pe care o vei folosi des: un pointer în interiorul unui șir este tot un șir, care se termină la același '\0'.

strcmp

Compară caracter cu caracter, de la stânga, până la prima diferență. Rezultatul e negativ dacă primul șir e mai mic lexicografic, 0 dacă sunt egale și pozitiv altfel. Nu te baza pe valoarea exactă (−1, −3 etc.), ci doar pe semn.

Comparația cu == între două tablouri char compară adrese, nu conținut. Pentru egalitate folosește întotdeauna strcmp(a, b) == 0.

strchr și strstr

Ambele returnează un pointer (adresa) sau NULL. Ca să obții poziția, scazi adresa de început a șirului:

char s[] = "informatica";
char *p = strchr(s, 'f');
if (p != NULL) cout << p - s;      // 2

char t[] = "bacalaureat";
char *q = strstr(t, "laur");
if (q != NULL) cout << q - t;      // 4

Un truc foarte util: strchr("aeiou", c) != NULL verifică dacă litera mică c este vocală, fără cinci comparații legate cu ||. Ai grijă să nu-l aplici pe caracterul '\0', pe care strchr îl găsește în orice șir.

Exemplu complet: numărarea aparițiilor unui cuvânt

Se citește un text și un cuvânt; afișăm de câte ori apare cuvântul ca secvență în text. După fiecare apariție găsită, continuăm căutarea de la caracterul următor.

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char s[256], c[31];
    cin.getline(s, 256);
    cin.getline(c, 31);
    int nr = 0;
    char *p = strstr(s, c);
    while (p != NULL)
    {
        nr++;
        p = strstr(p + 1, c);
    }
    cout << nr;
    return 0;
}

Pentru textul ana are banane și secvența an, programul afișează 3: o apariție în „ana” (poziția 0) și două în „banane” (pozițiile 9 și 11). Atenție la aparițiile suprapuse: în textul banana, secvența ana apare la pozițiile 1 și 3, care se suprapun, iar programul de mai sus afișează 2. Dacă enunțul cere apariții care nu se suprapun, continui căutarea cu p + strlen(c) în loc de p + 1 și obții 1.

Ștergerea unui caracter din șir

Ca să elimini caracterul de la poziția k, muți toate caracterele de după el cu o poziție la stânga, inclusiv terminatorul. Multe culegeri scriu strcpy(s + k, s + k + 1), dar standardul nu garantează că strcpy funcționează corect când sursa și destinația se suprapun. Varianta sigură folosește un șir auxiliar:

char aux[256];
strcpy(aux, s + k + 1);
strcpy(s + k, aux);

Și mai simplu, poți face deplasarea cu o buclă: for (i = k; s[i] != '\0'; i++) s[i] = s[i + 1]; — se oprește după ce a copiat și terminatorul.

La BAC: la itemii de tipul „ce se afișează”, scrie șirul pe hârtie cu indicele sub fiecare caracter. Majoritatea greșelilor vin din poziții numărate de la 1 în loc de 0 sau din uitarea faptului că strlen nu include terminatorul.

De reținut