Șiruri de caractere

Tabloul de caractere, terminatorul nul, citirea unei linii întregi și de ce nu folosim tipul string la bac.

Ce este un șir

Un șir de caractere este un tablou de char. Sfârșitul textului e marcat de un caracter special, '\0', numit terminator nul.

char s[101];

Declarația face loc pentru 101 caractere, adică un text de cel mult 100 de caractere plus terminatorul.

De aici regula de dimensionare: dacă enunțul spune „cel mult 200 de caractere", declari char s[201].

Terminatorul nul

char s[10] = "abc";

În memorie se află a, b, c, \0, iar restul nu contează.

Terminatorul nu se vede și nu intră în lungime, dar fără el nimic nu funcționează. Toate funcțiile de lucru cu șiruri se opresc la el. Un șir fără terminator e citit mai departe, în memoria de după, până se dă peste un zero întâmplător.

De aceea, dacă construiești un șir caracter cu caracter, trebuie să-l închizi tu:

char rez[101];
int k = 0;
for (int i = 0; s[i] != '\0'; i++)
    if (isalpha(s[i]))
        rez[k++] = s[i];
rez[k] = '\0';          // obligatoriu

Ultima linie se uită des. Îți dai seama după simptom: se afișează textul corect, urmat de caractere fără sens.

De ce nu string

C++ are un tip string, mult mai comod. Nu este în programa de bacalaureat, iar o soluție care îl folosește nu primește punctaj, la fel ca una cu vector, sort sau map. Pe această platformă, un item în care apare string primește direct 0.

La examen scrii în C/C++, dar din biblioteca standard C++ folosești doar cin/cout și fișierele. Rămân deci tablourile de char și funcțiile din <cstring>.

Citirea

Un singur cuvânt

char s[101];
cin >> s;

Citirea se oprește la primul spațiu. Observă că nu se scrie &s, pentru că numele tabloului este deja adresa lui.

O linie întreagă

cin.getline(s, 101);

Al doilea argument este dimensiunea tabloului, nu lungimea textului. Se citește tot până la linie nouă, inclusiv spațiile.

Capcana clasică: după un cin >> n, în flux rămâne caracterul de linie nouă. getline îl găsește imediat și întoarce un șir gol.

int n;
cin >> n;
cin.get();              // consumă linia nouă rămasă
cin.getline(s, 101);

Atenție: cin.getline(s, 101) este forma permisă. Forma getline(cin, s) are nevoie de tipul string și nu este acceptată.

Din fișier

Se face la fel, cu fin în loc de cin:

ifstream fin("date.in");
fin.getline(s, 101);

Parcurgerea

Ai două forme echivalente:

for (int i = 0; s[i] != '\0'; i++)
    // prelucrez s[i]
int n = strlen(s);
for (int i = 0; i < n; i++)
    // prelucrez s[i]

A doua e mai bună când ai oricum nevoie de lungime. Calculează strlen o singură dată, înaintea buclei. Dacă scrii așa:

for (int i = 0; i < strlen(s); i++)     // lent

lungimea se recalculează la fiecare pas, iar pentru asta se parcurge tot șirul. La un text lung, o parcurgere simplă devine pătratică.

Indicii pornesc de la 0. La șiruri nu are rost convenția de la 1. Toate funcțiile din bibliotecă lucrează de la 0, iar dacă amesteci cele două convenții apar erori.

Afișarea

cout << s;              // tot șirul, până la '\0'
cout << s[0];           // primul caracter

Un exemplu complet

Se citește o linie de text. Să se afișeze câte litere mari conține.

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

char s[256];

int main() {
    cin.getline(s, 256);

    int cate = 0;
    int n = strlen(s);
    for (int i = 0; i < n; i++)
        if (isupper(s[i]))
            cate++;

    cout << cate;
    return 0;
}

Modificarea pe loc

Un șir îl poți modifica direct, caracter cu caracter:

for (int i = 0; s[i] != '\0'; i++)
    s[i] = toupper(s[i]);

Dacă vrei să ștergi caractere, folosește tiparul cu doi indici de la vectori, unul care citește și unul care scrie:

int k = 0;
for (int i = 0; s[i] != '\0'; i++)
    if (s[i] != ' ')
        s[k++] = s[i];
s[k] = '\0';

Codul elimină toate spațiile într-o singură parcurgere, fără niciun tablou suplimentar.

De reținut

  • Un șir este un tablou de char încheiat cu '\0'.
  • Declară cu un loc mai mult decât lungimea maximă din enunț.
  • Când construiești un șir, pune tu terminatorul la final.
  • cin >> s ia un cuvânt, cin.getline(s, dim) ia o linie. După cin >> n, consumă linia nouă rămasă.
  • Nu folosi string. Nu e în programă și se punctează cu zero.
  • Calculează strlen o singură dată, în afara buclei.