4.10. Functiile standard sscanf si sprintf
2019/02/15 in Programare in C
Biblioteca standard a limbajelor C si C++ contine functiile sscanf si sprintf care sunt analoge functiilor scanf si printf. Ele au un parametru in plus la apel si anume primul lor parametru este adresa unei zone de memorie in care se pot pastra caractere ale codului ASCII. Ceilalti parametri sunt identici cu cei intalniti in corespondentele lor.
Primul parametru al acestor functii poate fi numele unui tablou de tip char, deoarece un astfel de nume are ca valoare chiar adresa de inceput a zonei de memorie care ii este alocata.
Functia sprintf se foloseste, ca si functia printf, pentru a realiza conversii ale datelor de diferite tipuri din formatele lor interne, in formate externe reprezentate prin succesiuni de caractere. Diferenta consta in aceea ca, de data aceasta caracterele respaective nu se afiseaza pe terminalul standard, ci se pastreaza in zona de memorie definita de primul parametru al functiei sprintf. Ele se pastreaza sub forma unui sir de caractere si pot fi afisate ulterior din zona respectiva cu ajutorul functiei puts. De aceea, un apel al functiei printf poate fi totdeauna inlocuit cu un apel al functiei sprintf, urmat de un apel al functiei puts. O astfel de inlocuire este utila cand dorim sa afisam de mai multe ori aceleasi date. In acest scop se apeleaza functia sprintf o singura data, pentru a face conversiile necesare din format intern in format extern, rezultatele conversiilor pastrandu-se intr-un tablou de tip caracter. In continuare se pot afisa datele respective apeland functia puts ori de cate ori este necesara afisarea lor. Functia sprintf, ca si functia printf returneaza numarul octetilor sirului de caractere rezultat in urma conversiilor efectuate.
Exemplu:
int zi, luna, an;
char data_calend[11];
...
sprintf(data_calend, "%02d/%02d/%d", zi, luna, an);
puts(data_calend);
...
puts(data_calend);
Functia sscanf realizeaza, ca si functia scanf, conversii din format extern in format intern. Deosebirea consta in faptul ca de aceasta data caracterele nu sunt citite din zona de tampon corespunzatoare tastaturii, ci ele provin dintr-o zona de memorie a carei adresa este definita de primul parametru al functiei sscanf. Aceste caractere pot proveni in zona respectiva in urma apelului functiei gets. In felul acesta, apelul functiei scanf poate fi inlocuit prin apelul functiei gets urmat de apelul functiei sscanf. Astfel de inlocuiri sunt utile cand dorim sa eliminam eventualele erori aparute la tastarea datelor.
Functia sscanf, la fel ca si functia scanf, returneaza numarul campurilor tratate corect. Analizand valoarea returnata, se poate stabili daca au fost prelucrate corect toate campurile sau a survenit o eroare.
In caz de eroare se poate reveni pentr a introduce corect datele respective. In acest scop este necesar sa se elimine caracterele incepand cu cel din pozitia eronata.
In cazul in care se utilizeaza secventa:
gets
sscanf
abandonarea caracterelor respective se face automat reapelandu-se functia gets. In cazul utilizarii functiei scanf este necesar sa se avanseze pana la caracterul newline aflat in zona tampon atasata tastaturii sau se videze zona respectiva prin functii speciale.
In exercitiile care urmeaza vom folosi secventele formate din apelurile functiei gets urmate de apelurile functiei sscanf. O astfel de secventa se apeleaza repetat in cazul in care se intalnesc erori in datele de intrare.
Exemplu:
char tab[255];
int zi, luna, an;
...
gets(tab);
sprintf(tab, "%d %d %d", &zi, &luna, &an);
Amintim ca functia gets returneaza valoarea NULL la intalnirea sfarsitului de fisier.
Functiile sscanf si sprinf au prototipurile in fisierul stdio.h.
Exercitii:
4.22. Sa se scrie un program care citeste un intreg pozitiv de tip long, stabileste daca acesta este prim si afiseaza un mesaj corespunzator.
- Programul 076 - Numar prim cu functia sscanf
#include <stdio.h>
#include <stdlib.h>
main()
{
long n;
long i;
int j;
char tab[255];
do {
printf("Tastati un numar intreg pozitiv: ");
if(gets(tab) == NULL) {
printf("s-a tastat EOF\n");
exit(1);
}
if (sscanf(tab, "%ld", &n) != 1 || n <= 0) {
printf("nu s-a tastat un numar intreg pozitiv\n");
j = 1;
continue; /* se va relua ciclul deoarece se trece la evaluarea /
expresiei j care este diferita de 0 */
}
j = 0; /* ciclul se intrerupe deoarece s-a citit corect un intreg pozitiv */
} while (j);
for(j = 1, i = 2; i*i <= n && j; i++)
if(n % i == 0) /* numarul nu este prim */
j = 0;
printf("numarul: %ld", n);
if (j == 0)
printf(" nu");
printf(" este prim\n");
}
Observatii:
- Utilizarea instructiunii continue se poate omite folosind o instructiune if cu alternativa else:
if(sscanf(...) != 1 || n <= 0) { ... j = 1; } else j = 0;
- Ciclul for continua atat timp cat expresia
i*i <= n && j
este adevarata. Aceasta expresie se evalueaza de la stanga spre dreapta si din aceasta cauza expresiai*i <= n
se evalueaza si candj = 0
. De aceea expresia respectiva este mai eficienta sub forma:j && i*i <= n
.In acest caz, pentru
j = 0
nu se mai evalueaza restul expresiei.Expresia
i*i <= n && j
rezulta din faptul ca, pentru a stabili ca un numar este prim, este suficient sa incercam divizibiltatea lui prin numere al caror patrat nu-l depaseste pe n.O alta posibilitate este aceea de ainlocui expresia
i*i <= n
cui <= sqrt(n)
.In acest caz este util sa se calculeze radacina patrata in afara ciclului for:
... long q; ... q = sqrt((double)n); for(j = 1, i = 2; j && i <= q; i++) if(n % i == 0) j = 0; ...
O alta simplificare posibila este schimbarea instructiunii if din corpul ciclului for cu:
j = n % i;
.