„Se citește un text de cel mult 100 de caractere, în care cuvintele sunt formate din litere mici ale alfabetului englez și sunt separate prin câte un spațiu…” — dacă ai rezolvat subiecte de BAC informatică, ai recunoscut deja începutul. Problemele cu cuvinte dintr-un text revin constant, iar în C++ ai două instrumente pentru ele: funcția strtok și parcurgerea manuală, caracter cu caracter. Fiecare are situațiile în care e alegerea bună.
Separarea cuvintelor cu strtok
strtok(s, sep) din <cstring> împarte șirul s în bucăți (numite token-uri), folosind ca separatori oricare dintre caracterele din sep. Se folosește în două feluri:
- la primul apel îi dai șirul:
p = strtok(s, " "); - la apelurile următoare îi dai
NULL, ca să continue de unde a rămas:p = strtok(NULL, " ");
Când nu mai sunt cuvinte, returnează NULL. Schema completă, care afișează fiecare cuvânt pe câte o linie și le numără:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s[101];
cin.getline(s, 101);
int nr = 0;
char *p = strtok(s, " ");
while (p != NULL)
{
cout << p << '\n';
nr++;
p = strtok(NULL, " ");
}
cout << nr;
return 0;
}
Pentru textul ana are mere se afișează „ana”, „are”, „mere” pe linii separate, apoi 3. Dacă textul conține și semne de punctuație, le pui în lista de separatori: strtok(s, " .,;!?"). Un avantaj în plus: strtok tratează corect și separatorii consecutivi (două spații la rând nu produc un cuvânt vid).
Ce face strtok „în spate”
strtok modifică șirul original: înlocuiește separatorul de după fiecare cuvânt cu '\0' și returnează adresa începutului de cuvânt. După ce ai terminat, în s a rămas doar primul cuvânt (dacă îl afișezi, vezi „ana”). De aceea:
- dacă mai ai nevoie de textul inițial, fă o copie cu
strcpyînainte; - nu poți modifica „pe loc” textul în timp ce îl spargi cu strtok și apoi să-l afișezi întreg — trebuie să construiești un text nou.
Construirea unui text nou din cuvinte
Cerință tipică: „Afișați textul obținut prin eliminarea cuvintelor care au mai puțin de 4 litere. Dacă nu rămâne niciun cuvânt, afișați mesajul nu exista.” Cu strtok iei fiecare cuvânt, iar pe cele bune le lipești cu strcat într-un șir rezultat, inițial vid:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s[101], rez[101] = "";
cin.getline(s, 101);
char *p = strtok(s, " ");
while (p != NULL)
{
if (strlen(p) >= 4)
{
if (rez[0] != '\0') strcat(rez, " ");
strcat(rez, p);
}
p = strtok(NULL, " ");
}
if (rez[0] == '\0') cout << "nu exista";
else cout << rez;
return 0;
}
Pentru la bac avem doar trei ore se afișează avem doar trei. Observă testul rez[0] != '\0': pune spațiul doar între cuvinte, nu și înaintea primului. E un detaliu pe care corectorii îl văd imediat.
Parcurgerea manuală: când strtok nu e potrivit
Dacă trebuie să modifici cuvintele în interiorul textului (să le inversezi, să schimbi prima literă, să dublezi o vocală), e mai simplu să parcurgi șirul caracter cu caracter. Ideea de bază: un caracter s[i] este începutul unui cuvânt dacă nu e spațiu și fie e pe poziția 0, fie înaintea lui e un spațiu.
for (i = 0; s[i] != '\0'; i++)
if (s[i] != ' ' && (i == 0 || s[i - 1] == ' '))
s[i] = s[i] - 'a' + 'A';
Secvența transformă prima literă a fiecărui cuvânt în majusculă: ana are mere devine Ana Are Mere. Analog, s[i] este sfârșitul unui cuvânt dacă nu e spațiu și după el urmează un spațiu sau '\0'.
Inversarea fiecărui cuvânt pe loc
Combinând cele două idei, găsești pentru fiecare cuvânt poziția de început st și de sfârșit dr, apoi interschimbi literele din capete spre mijloc:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s[101];
cin.getline(s, 101);
int n = strlen(s), i = 0, st, dr;
while (i < n)
{
while (i < n && s[i] == ' ') i++; // sar peste spații
st = i;
while (i < n && s[i] != ' ') i++; // merg până la finalul cuvântului
dr = i - 1;
while (st < dr)
{
char aux = s[st];
s[st] = s[dr];
s[dr] = aux;
st++;
dr--;
}
}
cout << s;
return 0;
}
Pentru bac la info se afișează cab al ofni. Spațiile rămân exact unde erau, pentru că lucrăm direct în șirul original. Urmărește un caz-limită: dacă textul se termină cu spațiu, după ultimul cuvânt st devine n, dr devine n−1, condiția st < dr e falsă și nu se întâmplă nimic — programul rămâne corect.
Cuvântul cel mai lung și alte cerințe clasice
Cu strtok, majoritatea cerințelor se reduc la o prelucrare a lui p în interiorul buclei:
- cel mai lung cuvânt: păstrezi lungimea maximă și copiezi cuvântul cu
strcpy(maxim, p)când găsești unul mai lung (cu>reții primul, cu>=ultimul — citește atent enunțul); - cuvinte care încep și se termină cu aceeași literă: compari
p[0]cup[strlen(p) - 1]; - cuvinte care conțin o anumită secvență:
strstr(p, "ab") != NULL; - ordinea alfabetică: compari cuvinte cu
strcmp, după ce le-ai copiat într-o matrice de caracterechar cuv[50][21].
strtok sau parcurgere manuală?
| Situație | Alegerea potrivită |
|---|---|
| Afișezi, numeri sau selectezi cuvinte | strtok |
| Construiești un text nou din unele cuvinte | strtok + strcat |
| Modifici literele cuvintelor în text | parcurgere manuală |
| Ai nevoie și de textul inițial după prelucrare | parcurgere manuală sau strtok pe o copie |
De reținut
- ● Primul apel:
strtok(s, sep); următoarele:strtok(NULL, sep); final: returneazăNULL. - ● strtok distruge textul original (pune
'\0'în locul separatorilor) — fă o copie dacă îți mai trebuie. - ● Un text nou se construiește pornind de la
rez[0] = '\0'și adăugând cu strcat, cu spațiu doar între cuvinte. - ● Început de cuvânt:
s[i] != ' 'și (i == 0saus[i-1] == ' '). - ● Pentru modificări pe loc (inversare, majuscule), parcurge manual cu doi indici st și dr.