Funcții pentru șiruri: copiere, căutare și strtok
Funcțiile din `<cstring>` lucrează cu șiruri char[] terminate prin caracterul zero. `strcpy` copiază, `strcat` concatenează, `strchr` caută un caracter, `strstr` caută un subșir, iar `strtok` extrage fragmente delimitate de separatori.
În acest material
Funcțiile din <cstring> lucrează cu șiruri char[] terminate prin caracterul zero. strcpy copiază, strcat concatenează, strchr caută un caracter, strstr caută un subșir, iar strtok extrage fragmente delimitate de separatori.
Aceste funcții nu cunosc automat capacitatea tabloului. Înțelegerea memoriei necesare este la fel de importantă ca memorarea numelor lor. Pentru std::string există atribuirea, operatorul +, find și substr, cu o interfață diferită.
Cum funcționează
Pentru copiere, destinația trebuie să aibă cel puțin lungimea sursei plus unu, pentru terminator. Pentru concatenare trebuie să încapă cele două texte și terminatorul final. strcpy și strcat nu sunt soluții generale pentru zone de memorie care se suprapun. Dacă sursa și destinația se intersectează, trebuie ales un algoritm potrivit, nu presupus că ordinea internă a copierii va salva rezultatul.
strchr(s,c) și strstr(s,t) întorc adresa primei apariții sau nullptr. Un pointer valid către mijlocul textului poate fi afișat ca sufix; pentru poziție calculăm diferența față de început numai după ce verificăm rezultatul. La std::string, find întoarce un indice sau npos, nu un pointer nul.
strtok primește la primul apel textul modificabil și mulțimea separatorilor. La apelurile următoare primește nullptr și continuă analiza aceluiași text. Funcția înlocuiește delimitatori cu zero, deci modifică originalul. Separatorii consecutivi nu generează cuvinte goale. În exemplu, spațiul, virgula, punctul și punctul și virgula sunt separatori individuali, nu un șablon care trebuie întâlnit integral.
Exemplu urmărit pas cu pas
Textul ana, are;mere. produce cuvintele ana, are și mere. După extragere, separatoarele relevante din char[] sunt înlocuite cu terminatori, deci o afișare obișnuită a întregului tablou nu mai arată textul inițial.
Varianta cu std::string caută începutul următorului cuvânt prin find_first_not_of și sfârșitul prin find_first_of. Extrage prin substr fără să modifice originalul. Ambele programe acceptă linii ASCII de cel mult 200 de caractere și afișează aceleași cuvinte și același număr.
Implementare C++17
Varianta clasică:
#include <cstring>
#include <iostream>
int main() {
char s[201];
std::cin.getline(s, 201);
int n = 0;
char *p = std::strtok(s, " ,.;");
while (p) {
++n;
std::cout << p << '\n';
p = std::strtok(nullptr, " ,.;");
}
std::cout << n << '\n';
}
Varianta cu std::string:
#include <iostream>
#include <string>
int main() {
std::string s;
std::getline(std::cin, s);
std::string sep = " ,.;";
int n = 0;
std::size_t start = s.find_first_not_of(sep);
while (start != std::string::npos) {
std::size_t end = s.find_first_of(sep, start);
++n;
std::cout << s.substr(start, end == std::string::npos ? end : end - start)
<< '\n';
if (end == std::string::npos)
break;
start = s.find_first_not_of(sep, end);
}
std::cout << n << '\n';
}
Copiere, concatenare și căutare într-un program complet
Citim două linii ASCII de cel mult 100 de caractere fiecare. Destinația clasică are 202 poziții: maximum 100+1+100 caractere vizibile și terminatorul zero. Copiem prima linie, adăugăm un spațiu și a doua linie. Apoi căutăm primul caracter a și prima apariție a celei de-a doua linii. Pozițiile sunt de la zero; -1 marchează absența. Pentru un subșir gol, căutarea găsește poziția zero, conform convenției ambelor interfețe.
Varianta std::string folosește atribuirea și concatenarea prin +. Ea gestionează memoria, dar păstrează aceleași limite de intrare pentru comparația exemplelor. Verificarea nullptr, respectiv npos, are loc înaintea calculului poziției. Copierea și concatenarea sunt liniare în lungimile manipulate; căutarea unui subșir nu trebuie confundată cu un simplu acces constant prin indice.
Varianta char[]:
#include <cstring>
#include <iostream>
int main() {
char a[101], b[101], joined[202];
std::cin.getline(a, 101);
std::cin.getline(b, 101);
std::strcpy(joined, a);
std::strcat(joined, " ");
std::strcat(joined, b);
std::cout << joined << '\n';
const char *c = std::strchr(joined, 'a');
const char *p = std::strstr(joined, b);
std::cout << (c ? c - joined : -1) << ' ' << (p ? p - joined : -1) << '\n';
}
Varianta std::string:
#include <iostream>
#include <string>
int main() {
std::string a, b;
std::getline(std::cin, a);
std::getline(std::cin, b);
std::string joined = a + " " + b;
std::cout << joined << '\n';
auto c = joined.find('a'), p = joined.find(b);
std::cout << (c == std::string::npos ? -1 : static_cast<long long>(c)) << ' '
<< (p == std::string::npos ? -1 : static_cast<long long>(p)) << '\n';
}
De ce funcționează și cât costă
Fiecare iterație identifică o secvență maximală de caractere care nu sunt separatori. Următoarea căutare începe după sfârșitul acelei secvențe, deci niciun caracter de cuvânt nu este omis sau inclus în două cuvinte.
Pentru o mulțime fixă și mică de separatori, parcurgerea are timp O(L), unde L este lungimea textului. Varianta cu substr creează copii ale cuvintelor afișate; spațiul temporar maxim depinde de cel mai lung cuvânt.
Greșeli frecvente
Nu apela strtok pe un literal constant și nu dereferenția nullptr. Nu compara rezultatul find cu -1 într-un tip nepotrivit; folosește std::string::npos. Nu confunda numărul de octeți cu numărul de litere pentru texte Unicode.
Exersează
Pentru textul ,,unu;doi,,, ce cuvinte se afișează? Ce trebuie schimbat dacă separatorul devine și caracterul două puncte?
Rezolvare explicată: Se afișează unu și doi, deci numărul este doi. Adaugi caracterul : în mulțimea separatorilor în ambele implementări, păstrând aceeași convenție.
Continuă să înveți
Reprezentarea textului, cuvinte și caractere ASCII.
Întrebări frecvente
Strtok modifică șirul?
Da. Introduce terminatori zero în textul primit. Dacă ai nevoie de forma originală, fă o copie într-un tablou suficient de mare înainte de separare.
Care este diferența dintre strchr și strstr?
strchr caută un singur caracter, iar strstr caută o secvență de caractere. Ambele întorc un pointer către prima apariție sau nullptr dacă nu găsesc.
Cât spațiu trebuie pentru strcat?
Destinația trebuie să poată păstra lungimea sa curentă, lungimea sursei și terminatorul final. Funcția nu verifică singură capacitatea disponibilă.
Ce înseamnă npos la std::string?
Este valoarea specială care indică absența unei poziții găsite. Verific-o înainte de a folosi rezultatul căutării ca indice în text.
Separatorii consecutivi produc cuvinte goale?
Nu în comportamentul strtok folosit aici. Dacă formatul datelor cere câmpuri goale, de exemplu într-un anumit CSV, ai nevoie de un parser cu alte reguli.
Un pas mai departe
Se încarcă progresul…