Programare Competitivă

Funcții din cstring

Am învățat să lucrăm cu șiruri de caractere manual: parcurgere, lungime, inversare, comparare - totul caracter cu caracter.

Biblioteca cstring (sau string.h) ne oferă funcții gata făcute care fac aceste operații mai ușor și mai rapid.

#include <cstring>

strlen - lungimea unui șir

int lungime = strlen(s);

Returnează numărul de caractere până la '\0' (fără a-l include).

#include <fstream>
#include <cstring>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

char s[101];

int main()
{
    fin >> s;
    fout << strlen(s);

    return 0;
}

date.in:

Informatica

date.out:

11

Echivalentul manual

// strlen face exact asta intern:
int lung = 0;
while (s[lung] != '\0') {
    lung++;
}

Atenție la performanță: strlen parcurge tot șirul la fiecare apel. Dacă o folosești în condiția unui for, se recalculează la fiecare iterație. Salvează rezultatul într-o variabilă:

// GREȘIT (lent):
for (int i = 0; i < strlen(s); i++) { ... }

// CORECT (rapid):
int lung = strlen(s);
for (int i = 0; i < lung; i++) { ... }

strcpy - copierea unui șir

strcpy(destinatie, sursa);

Copiază conținutul lui sursa în destinatie (inclusiv '\0').

char a[20] = "Salut";
char b[20];

strcpy(b, a);  // b devine "Salut"

fout << b;     // Salut

De ce avem nevoie?

Nu putem copia șiruri cu =:

char a[20] = "Salut";
char b[20];

b = a;          // GREȘIT! Nu compilează
strcpy(b, a);   // CORECT

Operatorul = nu funcționează pe vectori (și deci nici pe șiruri de caractere).


strcat - concatenarea (lipirea) a două șiruri

strcat(destinatie, sursa);

Adaugă conținutul lui sursa la sfârșitul lui destinatie.

char s[50] = "Buna ";

strcat(s, "ziua");
fout << s;       // Buna ziua

strcat(s, "!");
fout << s;       // Buna ziua!

Pas cu pas

Înainte:  s = "Buna "   (5 caractere + '\0')
strcat(s, "ziua"):
   - caută '\0' în s (poziția 5)
   - copiază "ziua" începând de la poziția 5
   - pune '\0' la final
După:     s = "Buna ziua" (9 caractere + '\0')

Atenție la dimensiune

Vectorul destinație trebuie să fie suficient de mare pentru a încăpea ambele șiruri:

char s[5] = "Buna";
strcat(s, " ziua");  // GREȘIT! Depășire - s are doar 5 poziții

strcmp - compararea a două șiruri

int rezultat = strcmp(a, b);

Compară lexicografic (ca în dicționar) și returnează:

Rezultat Semnificație
0 Șirurile sunt egale
< 0 (negativ) a este înaintea lui b (a < b)
> 0 (pozitiv) a este după b (a > b)
char a[] = "abc";
char b[] = "abd";

int r = strcmp(a, b);

if (r == 0) {
    fout << "Egale";
} else if (r < 0) {
    fout << "a vine inainte de b";
} else {
    fout << "a vine dupa b";
}

Output: a vine inainte de b (pentru că 'c' < 'd')

Exemple

a b strcmp(a, b) Explicație
"abc" "abc" 0 Identice
"abc" "abd" < 0 'c' < 'd'
"abd" "abc" > 0 'd' > 'c'
"ab" "abc" < 0 "ab" e mai scurt
"abc" "ab" > 0 "abc" e mai lung
"Ana" "ana" < 0 'A' (65) < 'a' (97)

Important: literele mari au coduri ASCII mai mici decât cele mici, deci "Ana" < "ana".

Verificare egalitate: nu folosi == pe șiruri! Folosește strcmp(a, b) == 0:

if (strcmp(s, "parola") == 0) {
    fout << "Acces permis";
}

strchr - căutarea unui caracter

char *poz = strchr(s, 'a');

Caută prima apariție a caracterului 'a' în șirul s. Returnează un pointer către acea poziție, sau NULL dacă nu există.

char s[] = "Informatica";

char *p = strchr(s, 'a');

if (p != NULL) {
    fout << "Gasit la pozitia " << (p - s);
} else {
    fout << "Nu exista";
}

Output: Gasit la pozitia 6

De ce p - s?

p este un pointer către caracterul găsit, s este un pointer către începutul șirului. Diferența p - sindicele (poziția) în vector.


strstr - căutarea unui subșir

char *poz = strstr(s, sub);

Caută prima apariție a subșirului sub în șirul s.

char s[] = "Buna ziua frumoasa";
char *p = strstr(s, "ziua");

if (p != NULL) {
    fout << "Gasit la pozitia " << (p - s);
} else {
    fout << "Nu exista";
}

Output: Gasit la pozitia 5


strtok - împărțirea în cuvinte (tokenizare)

char *cuv = strtok(s, separatori);

Împarte șirul s în “tokeni” (cuvinte) pe baza separatorilor dați.

#include <fstream>
#include <cstring>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

char s[1001];

int main()
{
    fin.getline(s, 1001);

    char *cuv = strtok(s, " ");

    while (cuv != NULL) {
        fout << cuv << endl;
        cuv = strtok(NULL, " ");
    }

    return 0;
}

date.in:

Ana are mere rosii

date.out:

Ana
are
mere
rosii

Cum funcționează?

  1. Primul apel: strtok(s, " ") - caută primul cuvânt din s, despărțit de spații.
  2. Apelurile următoare: strtok(NULL, " ") - continuă căutarea de unde a rămas.
  3. Când nu mai sunt cuvinte, returnează NULL.

Separatori multipli

Putem specifica mai mulți separatori:

char *cuv = strtok(s, " ,.;:!?");

Aceasta împarte textul la spații, virgule, puncte, etc.

Cum funcționează strtok intern?

strtok modifică șirul original! Înlocuiește fiecare separator găsit cu '\0', transformând efectiv șirul într-o serie de subșiruri.

De exemplu, "Ana are mere" devine:

A n a \0 a r e \0 m e r e \0

De aceea:

  • Nu folosi strtok pe un șir constant ("text")
  • Dacă ai nevoie de șirul original, fă o copie înainte

La apelurile ulterioare cu NULL, funcția își amintește intern unde a rămas.


Tabel rezumativ

Funcție Ce face Exemplu
strlen(s) Lungimea șirului strlen("abc") = 3
strcpy(dest, src) Copiază src în dest strcpy(b, a)
strcat(dest, src) Lipește src la sfârșitul dest strcat(s, "!")
strcmp(a, b) Compară a cu b 0 dacă egale
strchr(s, c) Caută caracterul c Pointer sau NULL
strstr(s, sub) Caută subșirul sub Pointer sau NULL
strtok(s, sep) Împarte în cuvinte Pointer sau NULL

Problema completă: sortarea cuvintelor

Citim o propoziție și afișăm cuvintele în ordine alfabetică.

#include <fstream>
#include <cstring>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

char s[1001];
char cuvinte[100][101]; // maxim 100 de cuvinte, fiecare de maxim 100 caractere
int nrCuv;

int main()
{
    fin.getline(s, 1001);

    // Împărțim în cuvinte
    char *p = strtok(s, " ");

    while (p != NULL) {
        strcpy(cuvinte[nrCuv], p);
        nrCuv++;
        p = strtok(NULL, " ");
    }

    // Sortăm (bubble sort pe șiruri)
    for (int i = 0; i < nrCuv - 1; i++) {
        for (int j = i + 1; j < nrCuv; j++) {
            if (strcmp(cuvinte[i], cuvinte[j]) > 0) {
                char aux[101];
                strcpy(aux, cuvinte[i]);
                strcpy(cuvinte[i], cuvinte[j]);
                strcpy(cuvinte[j], aux);
            }
        }
    }

    // Afișăm
    for (int i = 0; i < nrCuv; i++) {
        fout << cuvinte[i] << " ";
    }

    return 0;
}

date.in:

mere ana banane cireasa ana

date.out:

ana ana banane cireasa mere

Ce observăm?

  • Interschimbarea șirurilor se face cu strcpy (prin variabila auxiliară aux), nu cu =
  • Compararea se face cu strcmp, nu cu < sau >
  • cuvinte[100][101] este o matrice de caractere: fiecare linie este un cuvânt

Greșeli frecvente

1. Lipsa lui #include <cstring>

Fără această bibliotecă, funcțiile strlen, strcpy, etc. nu sunt recunoscute.


2. Depășire la strcat și strcpy

char s[5] = "ABC";
strcat(s, "DEFGH"); // GREȘIT! Depășirea vectorului

Vectorul destinație trebuie să fie suficient de mare.


3. Compararea cu == în loc de strcmp

if (a == b)           // GREȘIT - compară adresele
if (strcmp(a, b) == 0) // CORECT - compară conținutul

4. strtok pe șir constant

strtok("Ana are", " "); // GREȘIT! Modifică un șir constant

strtok modifică șirul original. Folosește-l doar pe vectori char[].


5. Uitarea lui NULL la apelurile ulterioare strtok

char *p = strtok(s, " ");    // primul apel: cu s
p = strtok(s, " ");           // GREȘIT! Reîncepe de la capăt
p = strtok(NULL, " ");        // CORECT! Continuă de unde a rămas

6. strlen în condiția for

for (int i = 0; i < strlen(s); i++) // LENT! strlen se recalculează la fiecare pas

Salvează lungimea într-o variabilă înainte de buclă.


Ce să reții

  • Biblioteca cstring oferă funcții gata făcute pentru operații pe șiruri.
  • strlen - lungimea, strcpy - copiere, strcat - concatenare.
  • strcmp - comparare (returnează 0 dacă egale, < 0 sau > 0 altfel).
  • strchr - caută caracter, strstr - caută subșir, strtok - împarte în cuvinte.
  • Nu folosim = pentru copiere sau == pentru comparare pe șiruri.
  • strtok modifică șirul original și folosește NULL la apelurile ulterioare.
  • Mereu verificăm dimensiunile vectorilor la strcpy și strcat.