Șiruri de caractere
Până acum am lucrat cu numere. Dar în multe probleme trebuie să prelucrăm text: cuvinte, propoziții, nume.
În C++, un text se stochează ca un șir de
caractere - un vector de tip char terminat
cu un caracter special.
Ce este un caracter?
Un caracter este o singură literă, cifră sau
simbol. În C++ îl declarăm cu tipul char și îl
scriem între apostrofuri:
char c = 'A';
char cifra = '5';
char spatiu = ' ';
char newline = '\n';Fiecare caracter are un cod numeric (codul ASCII). Câteva exemple:
| Caracter | Cod ASCII |
|---|---|
'A' |
65 |
'B' |
66 |
'Z' |
90 |
'a' |
97 |
'z' |
122 |
'0' |
48 |
'9' |
57 |
' ' (spațiu) |
32 |
Observații importante: literele mari
(A-Z) au codurile 65-90, literele mici
(a-z) au codurile 97-122. Diferența între o literă
mică și litera mare corespunzătoare este mereu
32: 'a' - 'A' = 32.
Operații pe caractere
Deoarece caracterele sunt de fapt numere (coduri ASCII), putem face operații cu ele:
char c = 'A';
c = c + 1; // c devine 'B' (65 + 1 = 66)Verificare: este literă mare?
if (c >= 'A' && c <= 'Z') {
// c este literă mare
}Verificare: este cifră?
if (c >= '0' && c <= '9') {
// c este cifră
}Conversie majusculă - minusculă
char mare = 'A';
char mica = mare + 32; // 'a'
char mica2 = 'g';
char mare2 = mica2 - 32; // 'G'De ce 32?
În tabelul ASCII, literele mari și mici sunt aranjate în aceeași ordine, dar la o distanță de 32:
'A'= 65,'a'= 97, diferența = 32'B'= 66,'b'= 98, diferența = 32- …și tot așa
Deci: literă_mică = literă_mare + 32 și
literă_mare = literă_mică - 32.
O altă formă, mai elegantă:
literă_mică = literă_mare + ('a' - 'A').
Ce este un șir de caractere?
Un șir de caractere (string) este un vector
de char terminat cu caracterul special
'\0' (null terminator, codul ASCII
0).
char salut[10] = "Buna";În memorie arată așa:
indice: 0 1 2 3 4 5 6 ...
+----+----+----+----+----+----+----+
| B | u | n | a | \0 | | |
+----+----+----+----+----+----+----+
- Cuvântul
"Buna"are 4 caractere vizibile - Pe poziția 4 este
'\0'care marchează sfârșitul șirului - Pozițiile 5, 6, … nu sunt folosite
'\0'este esențial. Fără el, programul nu știe unde se termină textul.
Declararea șirurilor
char s[101]; // șir gol, poate stoca până la 100 de caractere + '\0'
char s[] = "Salut"; // compilatorul calculează dimensiunea automat (6: 5 + '\0')
char s[20] = "Test"; // 20 de poziții, doar primele 5 folositeDimensiunea: dacă vrem să stocăm maxim
n caractere, declarăm vectorul de
n + 1 poziții (pentru '\0').
Citirea și afișarea
Citirea unui cuvânt (fără spații)
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
char s[101];
int main()
{
fin >> s;
fout << s;
return 0;
}fin >> s citește caractere până la primul
spațiu sau sfârșit de linie.
date.in:
Informaticadate.out:
InformaticaCitirea unei linii întregi (cu spații)
fin.getline(s, 101);getline citește toată linia, inclusiv spațiile,
până la '\n' sau până la limita dată (101).
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
char s[101];
int main()
{
fin.getline(s, 101);
fout << s;
return 0;
}date.in:
Salut lume frumoasadate.out:
Salut lume frumoasaLungimea unui șir
Lungimea se calculează numărând caracterele până la
'\0':
char s[] = "Algoritm";
int lung = 0;
while (s[lung] != '\0') {
lung++;
}
fout << lung; // 8Parcurgerea unui șir
Parcurgem caracter cu caracter, de la 0 până la
'\0':
for (int i = 0; s[i] != '\0'; i++) {
fout << s[i] << " ";
}Exemplu: pentru "ABC" afișează:
A B C
Probleme clasice cu șiruri
Problema 1: câte vocale are un cuvânt?
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
char s[101];
int main()
{
fin >> s;
int cnt = 0;
for (int i = 0; s[i] != '\0'; i++) {
char c = s[i];
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ||
c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
cnt++;
}
}
fout << cnt;
return 0;
}date.in:
Informaticadate.out:
5Problema 2: inversarea unui șir
Interschimbăm primul caracter cu ultimul, al doilea cu penultimul, etc.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
char s[101];
int main()
{
fin >> s;
// Aflăm lungimea
int lung = 0;
while (s[lung] != '\0') lung++;
// Inversăm
int st = 0, dr = lung - 1;
while (st < dr) {
char aux = s[st];
s[st] = s[dr];
s[dr] = aux;
st++;
dr--;
}
fout << s;
return 0;
}date.in:
algoritmdate.out:
mtiroglaProblema 3: este palindrom?
Un cuvânt este palindrom dacă citit invers este identic.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
char s[101];
int main()
{
fin >> s;
int lung = 0;
while (s[lung] != '\0') lung++;
int palindrom = 1;
int st = 0, dr = lung - 1;
while (st < dr) {
if (s[st] != s[dr]) {
palindrom = 0;
}
st++;
dr--;
}
if (palindrom == 1) {
fout << "DA";
} else {
fout << "NU";
}
return 0;
}date.in:
abacabadate.out:
DAProblema 4: transformare în majuscule
for (int i = 0; s[i] != '\0'; i++) {
if (s[i] >= 'a' && s[i] <= 'z') {
s[i] = s[i] - 32;
}
}Problema 5: numărarea cuvintelor dintr-o propoziție
Cuvintele sunt separate prin spații. Numărăm tranzițiile de la spațiu la non-spațiu.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
char s[1001];
int main()
{
fin.getline(s, 1001);
int cnt = 0;
for (int i = 0; s[i] != '\0'; i++) {
if (s[i] != ' ' && (i == 0 || s[i - 1] == ' ')) {
cnt++;
}
}
fout << cnt;
return 0;
}date.in:
Ana are mere multedate.out:
4Cum funcționează?
Un cuvânt nou începe când: - Caracterul curent
nu este spațiu (s[i] != ' ') -
Și fie suntem la începutul șirului
(i == 0), fie caracterul anterior
era spațiu (s[i-1] == ' ')
Compararea a două șiruri
Nu putem compara șiruri cu == (aceasta compară
adresele, nu conținutul). Trebuie comparate caracter cu
caracter:
char a[] = "abc";
char b[] = "abd";
int i = 0;
while (a[i] != '\0' && b[i] != '\0' && a[i] == b[i]) {
i++;
}
if (a[i] == b[i]) {
fout << "Egale";
} else if (a[i] < b[i]) {
fout << "a < b (ordine lexicografica)";
} else {
fout << "a > b";
}Compararea se face lexicografic (ca în dicționar): se caută primul caracter diferit, iar cel cu codul ASCII mai mic este “mai mic”.
Greșeli frecvente
1. Depășirea vectorului
char s[5] = "Salut"; // GREȘIT! "Salut" are 5 caractere + '\0' = 6
char s[6] = "Salut"; // CORECT2. Compararea cu ==
if (s == "test") // GREȘIT - compară adresele, nu conținutulTrebuie comparat caracter cu caracter (sau cu
strcmp din cstring, vezi lecția
următoare).
3. Uitarea lui '\0'
Dacă construim un șir manual (caracter cu caracter), trebuie
să punem '\0' la final:
char s[10];
s[0] = 'A';
s[1] = 'B';
s[2] = '\0'; // OBLIGATORIU!Fără '\0', funcțiile care lucrează cu șiruri nu
știu unde se termină textul.
4. fin >> s
nu citește spații
Dacă intrarea este "Ana are",
fin >> s citește doar "Ana".
Pentru toată linia, folosim
fin.getline(s, dimensiune).
Ce să reții
- Un caracter (
char) este o literă/cifră/simbol cu un cod ASCII numeric. - Un șir de caractere este un vector de
charterminat cu'\0'. - Dimensiunea vectorului trebuie să fie cu 1 mai mare decât lungimea maximă a textului.
- Parcurgem cu
for (int i = 0; s[i] != '\0'; i++). fin >> scitește un cuvânt,fin.getline(s, dim)citește o linie întreagă.- Caracterele sunt numere - putem face operații aritmetice și comparații pe ele.
- Nu comparăm șiruri cu
==- comparăm caracter cu caracter. - Mereu punem
'\0'la finalul unui șir construit manual.