Siruri de caractere


Introducere

În C++ există mai multe modalități de a reprezenta șirurile de caractere. În acest articol vom discuta despre șirurile de caractere reprezentate ca tablouri unidimensionale cu elemente de tip char, reprezentare care provine din limbajul C. Aceste șiruri se mai numesc null-terminated byte string (NTBS). În reprezentarea internă, după ultimul caracter (byte, octet) valid din șir se află caracterul '\0' – caracterul cu codul ASCII 0, numit și caracter nul. Astfel, pentru reprezentarea în C/C++ a cuvântului copil, care are 5 caractere, se vor folosi 6 octeți, cu valorile: 'c', 'o', 'p', 'i', 'l', '\0'.

Declararea unui sir de caractere

Un șir de caractere se declară în C++ astfel:
char s[11]; S-a declarat un șir care poate memora maxim 11 caractere, cu indici 0 1 ... 10. Șirul s poate memora cel mult 10 caractere utile, după ultimul caracter util fiind memorat caracterul '\0'. De asemenea, la declararea unui șir acesta poate fi inițializat. Următoarele exemple declară șiruri de caractere și le inițializează cu șirul "copil":

char s[11] = "copil"; // se folosesc doar 6 caractere
char t[]="copil"; // se aloca automat 6 octeti pentru sirul t: cele 5 litere si caracterul nul \0
char x[6]={'c','o','p','i','l','\0'}; // initializarea este similara cu cea a unui tablou oarecare - sirurile de caractere sunt tablouri
char z[]={'c','o','p','i','l','\0'}; // se aloca automat 6 octeti pentru sir

Afișarea unui șir de caractere Se poate face cu operatorul << de inserție în stream: cout << s << endl; Citirea unui șir de caractere Se poate folosi operatorul >> de extracție din stream: cin >> s; În acest mod, datorită specificului operatorului >> nu se pot citi șiruri care conțin spații – se vor citi caracterele până la primul spațiu, fără acesta. Pentru a citi șiruri care conțin spații, putem folosi metoda getline a obiectului cin sau alt obiect de tip istream: istream& getline (char* s, streamsize n ); Se vor citi în șirul s caracterele din stream-ul de intrare (de la tastatură) până la apariția caracterului sfârșit de linie '\n', dar nu mai mult de n-1 caractere. Caracterul '\n' nu va fi adăugat la șirul s, dar va fi extras din stream. De exemplu: cin.getline(s , 11); Am putea spune că getline citește toată linia și sare peste ENTER. Iată un exemplu complet: #include < iostream> using namespace std; int main(){ char nume1[31], nume2[31]; cout << "Cum te cheama? (nume, prenume) "; cin.getline(nume1, 31); cout << "Cum il cheama pe prietenule tau? "; cin.getline(nume2 , 31); cout << "Te numesti " << nume1 << endl; cout << "Esti prieten cu " << nume2 << endl; return 0; } O altă modalitate de citire a unui șir care poate conține spații este folosirea metodei get a obiectului istream, pe care nu o mai prezentăm aici. Referirea unui caracter din șir. Parcurgerea unui șir de caractere Deoarece șirurile de caractere sunt de fapt tablouri, pentru referirea unui caracter din șir se folosește operatorul [], ca în exemplul următor: char s[]="abac"; // sirul consta din 5 caractere: cele 4 litere si caracterul nul '\0' cout << s[3]; // c s[1] = 'r'; cout << s; // arac cout << s[10]; // ??? comportament impredictibil: nu exista in sir caracter cu indice 10 În numeroase situații este necesară analizarea fiecărui caracter din șir. Pentru aceasta este necesară o parcurgere a șirului; aceasta se face similar cu parcurgerea unui tablou oarecare. Diferența constă în faptul că, pentru șirul de caractere nu se cunoaște explicit lungimea. Ea poate fi determinată cu funcția strlen (vezi mai jos), dar putem controla parcurgerea șirului știind că după ultimul caracter valid din șir apare caracterul nul '\0'. Următoarele exemple parcurg un sir de caractere și afișează caracterele separate prin spații: char s[11]; cin >> s; // se citeste un cuvant , fara spatii int i = 0; while(s[i] != '\0') { cout << s[i] << " "; i ++; } sau mai condensat: char s[11]; cin >> s; // se citeste un cuvant , fara spatii for(int i = 0 ; s[i] ; i ++) cout << s[i] << " ";

Probleme rezolvate

Problema 1
Din fisierul text.in se citeste un text aflat pe mai multe linii si care este format din litere ale alfabetului englez, caractere spatiu si NewLine. In fisierul text.out se va afisa textul initial in care toate cuvintele de lungime maxima vor fi inlocuite cu inversul (oglinditul) lor. Restul cuvintelor si arangarea lor pe linii vor ramane neschimbate. Numarul total de caractere din fisier este cel mult 5000.
Exemplu:
text.in
Eu am gandit la fel ca tine
Dar am gresit codul
text.out
Eu am tidnag la fel ca tine
Dar am tiserg codul

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

int main()
{
    char s[5001];
    int lmax=0;
    fin.get(s,5001,EOF); //citesc tot fisierul
    //calculez lungimea maxima a unui cuvant
    for(int i=0;s[i];i++)
        if(s[i]!=' ' && s[i]!='\n')
        {
            int j=i;
            while(s[j] && s[j]!=' ' && s[j]!='\n')
                j++;
            if(j-i>lmax) lmax=j-i;
        }
    //oglindesc cuvintele de lungime maxima
    for(int i=0;s[i];i++)
        if(s[i]!=' ' && s[i]!='\n')
        {
            int j=i;
            while(s[j] && s[j]!=' ' && s[j]!='\n')
                j++;
            if(j-i==lmax)
            {
                for(int x=i,y=j-1;x<y;x++,y--)
                    swap(s[x],s[y]);
            }
        }
    fout << s;
    return 0;
}
    

Problema 2

Se citeste un cuvant s format din cel mult 100 de litere mici. Se citeste un numar natural p (p<=100). Afisati cuvintele obtinute prin eliminarea unei secvente de p litere din s.
Exemplu: s="adina", si p=3, rezulta cuvintele:
na aa ad

Rezolvare
#include < iostream>
#include < cstring>
    using namespace std;
    
    int main()
    {
        char s[101];
        int p;
        cin>>s>>p;
        for(int i=0;i<=strlen(s)-p;i++)
        {
            char t[101], aux[101];
            strcpy(t,s);
            strcpy(aux,s+i+p);
            strcpy(t+i,aux);
            cout<< t <<" ";
        }
        return 0;
    }

Probleme propuse

1) Se citesc doua cuvinte a si b, fiecare fiind format din cel mult 20 de litere mici. Afisati pe randuri separate si se parate prin cate un spatiu:
- literele care apar in ambele cuvinte
- literele care apar in cel putin unul dintre cuvinte
- literele care apar in doar unul dintre cuvinte

Exemplu:
    pentru cuvintele adina si alina se va afisa:
    a i n
    a d i l n
    d l
    

2) Se citeste o propozitie cu maxim 200 de litere si spatii si apoi o silaba formata din exact 2 litere. Calculati si afisati numarul de aparitii ale silabei in propozitie.

Exemplu:
        ana are mere
        re
        =>2

3) Se citeste un cuvant format din cel mult 20 de litere. Sa se intershimbe prima jumatate a cuvantului cu cea de a doua. Daca cuvantul este format din numar impar de litere, atunci litera din mijloc va ramane pe loc.

Exemple: Cuvantul "cada" se transforma in "daca", iar "alina" in "naial".

4) Se citeste un numar natural n si apoi n cuvinte formate din cel mult 20 de litere fiecare. Calculati si afisati cate dintre cele n-1 cuvinte citite incepand cu cel de al doilea au proprietatea ca primul cuvant citit le este sufix.

 Exemplu: daca n=6, iar cuvintele citite sunt ion,revelion, ionel, bulion, milion,
        pionul => 3 (3 cuvinte din ultimele 5 se termina cu sufixul ion).

5) Se citesc doua cuvinte a si b formate din cel mult 20 de litere fiecare. Afisati toate sufixele cuvantului a care au proprietatea ca sunt prefixe ale cuvantului b. Daca nu exista astfel de sufixe afisati mesajul "nu exista".

 Exemplu: pentru cuvintele a="rebele" si b="elegant" sufixele cerute sunt "ele" si
         "e" (nu neaparat in aceasta ordine).