Documente Academic
Documente Profesional
Documente Cultură
F işiere
V I .1 .1 N o Ń i u ne a d e f i ş i e r d e d a t e
387
de fiecare dată când vrea să ruleze programul, spre exemplu fie şi numai pentru a
căuta o anumită carte ? Ar fi absurd ! Nu există decât o singură soluŃie: datele
respective să fie memorate într-un fişier, pe un disc, unde vor fi păstrate în
permanenŃă. Ajungem astfel la concluzia că şi datele cărŃilor din bibliotecă pot fi
păstrate într-un aşa-numit fişier de date.
Sistemul de operare "vede" fişierele de date la fel ca pe orice alte fişiere
(recunoscându-le, reamintim, prin numele lor), motiv pentru care şi asupra
acestora se pot aplica operaŃiile care se pot realiza cu orice fel de fişiere sub
controlul sistemului de operare: copiere, mutare, ştergere, redenumire etc.
În plus însă, un fişier de date poate fi scris sau citit de către utilizator.
Astfel, revenind la exemplul cu biblioteca:
informaŃiile despre cărŃi vor fi scrise o singură dată într-un fişier de
date, unde vor fi păstrate în permanenŃă pe disc;
de fiecare dată când este rulat, în loc să citească datele de la tastatură,
programul le va citi din respectivul fişier.
Categoric în acest moment aveŃi o nelămurire: cum poate fi scris un fişier
de date ? Răspunsul este foarte simplu: fie manual (prin intermediul unui editor de
texte, de exemplu poate fi folosit chiar editorul limbajului Pascal în care vă scrieŃi
sursele programelor), fie prin intermediul unui alt program.
Despre modul cum un program poate sa scrie într-un fişier de date sau
poate să citească dintr-un astfel de fişier, vom învăŃa în lecŃiile următoare.
V I .1 . 2 . C l a s i f i c a r e a f i ş i e r e l o r d e da t e
388
VI . 1 .3 . N o Ń i un e a d e f i ş i e r t e x t
Un fişier text conŃine una sau mai multe linii de caractere de lungime
variabilă. O astfel de linie se mai numeşte rând în fişier. Fiecare rând, mai puŃin
ultimul, se încheie printr-un "marcaj de sfârşit de linie" alcătuit din caracterele CR
("Carriadge Return ") şi LF ("Line Feed "). Sfârşitul fişierului este marcat prin
caracterul EOF ("End Of File ").
Caracterele dintr-un fişier text pot fi atât caractere tipăribile (litere mari şi
mici, cifre, caractere speciale), cât şi caractere netipăribile. Acestea din urmă se
mai numesc şi caractere albe (ENTER, LF, SPACE, BACKSPACE, TAB etc.).
Într-un fişier text, din cauză că rândurile de caractere au lungimi diferite,
nu putem identifica poziŃia unui anumit caracter faŃă de începutul fişierului. De
aceea, accesul într-un fişier text se face secvenŃial: pentru a ajunge la un caracter
oarecare din interiorul fişierului trebuie să "trecem" peste toate caracterele aflate
înaintea acelui caracter în fişier.
Pentru a putea prelucra datele dintr-un fişier text acesta trebuie deschis, iar
după încheierea prelucrării fişierul trebuie închis la loc.
Orice fişier text se caracterizează printr-o variabilă specială numită
indicator de fişier. Acest indicator "arată" în orice moment primul caracter din
fişier ce poate fi prelucrat.
V I .1 .4 . Cr e a r e a , de s c h i de r e a ş i î n c h i d e r e a
unui fi ş ie r t ex t
Aşa cum am mai spus, orice fişier, inclusiv un fişier text, se caracterizează
printr-un nume sub care este identificat de către sistemul de operare (alcătuit din
două părŃi: numele propriu-zis dat de utilizator, şi extensia care indică tipul
fişierului, separate prin caracterul "punct ". De obicei, pentru fişierele text se
foloseşte extensia txt (de la "text") sau dat (de la "fişier de date"). De exemplu:
numere.txt, ecuatii.dat etc.
Pe de altă parte, un program C++ nu este capabil să recunoască numele
sub care este cunoscut un fişier de către sistemul de operare, motiv pentru care
avem nevoie de o variabilă care să identifice fişierul. O astfel de variabilă trebuie
declarată, la fel ca orice variabilă, în secŃiunea de declaraŃii a programului.
Variabilele care desemnează fişierele se numesc descriptori de fişier, şi aparŃin
unui tip de date predefinit, numit "tipul pointer către fişier". Astfel, dacă avem
două fişiere şi notăm cu f şi g descriptorii ataşaŃi lor, atunci declaraŃiile
descriptorilor ca variabile arată astfel:
389
Ex e mp l u : FILE *f,*g;
Pentru a vă face să înŃelegeŃi ce înseamnă tipul de date "pointer către tipul
fişier" şi ce semnificaŃie are caracterul "*" plasat în faŃa descriptorilor f şi g, ar
trebui să vă prezentăm toată teoria pointerilor în C++. Aceasta însă depăşeşte cu
mult cadrul materiei de clasa a IX-a, motiv pentru care singura soluŃie este, cel
puŃin deocamdată, să luaŃi lucrurile ca atare. În clasa a XI-a, când veŃi studia
capitolul "Pointeri", veŃi înŃelege în profunzime sensul declaraŃiilor de variabile de
genul celei de mai sus, dar până atunci va trebui să le folosiŃi pur şi simplu, fără să
vă mai bateŃi capul cu interpretarea lor.
Pentru a putea folosi efectiv un fişier text într-un program, trebuie făcută o
legătură între descriptorul de fişier (fişierul logic) şi numele sub care este
recunoscut de către sistemul de operare (fişierul fizic). Această legătură se numeşte
asignarea fişierului.
390
Pentru a deschide fişierul "fis1.txt" cu atributul "citire", asignând totdată
acestuia descriptorul f, vom scrie instrucŃiunea:
f=fopen("C:\\ELEVI\\CLASA_X\\fis1.txt","r");
Dacă fişierul "fis1.txt" s-ar fi aflat în directorul curent, era suficient
f=fopen("fis1.txt","r");
Ca să deschidem fişierul "fis2.txt" cu atributul "scriere" şi să asignăm
acestuia descriptorul g este necesară instrucŃiunea:
g=fopen("C:\\ELEVI\\CLASA_X\\fis2.txt","w");
Sintaxa: fclose(<descriptor>);
Exemplu:
Pentru a închide fişierele cu descriptorii f şi g deschise în exemplul anterior, vom scrie:
fclose (f);
fclose (g);
Nu putem închide deodată ambele fişiere printr-o instrucŃiune de genul
{fclose(f,g);}.
Ex e mp l u :
if (feof(f)) // cu sensul “dacă feof(f) este diferit de zero”
.......
► 4. Din cine este alcătuit marcajul de sfârşit de rând într-un fişier text ?
► 10. Pentru fiecare dintre afirmaŃiile de mai jos, răspundeŃi cu "adevărat" sau "fals".
a) Un rând al unui fişier text poate conŃine cel mult 255 de caractere.
b) Un fişier text poate conŃine caractere tipăribile şi caractere albe.
c) Într-un fişier text datele sunt memorate sub formă de blocuri de octeŃi de lungime variabilă.
d) Următoarea declaraŃie de variabilă defineşte corect descriptorul f aferent unui fişier text:
FILE *f;
e) Următoarea instrucŃiune asignează corect descriptorul f fişierului nr.dat.
f=fopen("nr.dat","r");
Dintr-un fişier text putem citi valori ale unor variabile de tip caracter, şir
de caractere şi numeric. Deorece şirurile de caractere vor face obiectul unui capitol
din clasa a X-a, deocamdată ne ocupăm doar de citirea variabilelor de tip caracter
şi a celor numerice.
Exemplu: &$?
#*
Fie fişierul cu descriptorul f având următorul conŃinut:
# include <stdio.h> InstrucŃiunea
# include <iostream.h> {f=fopen("C:\\car.txt","r");} asignează
char c; descriptorul f fişierului "car.txt" aflat în
FILE *f; rădăcina partiŃiei C, şi deschide acest fişier cu
void main ()
atributul "r", adică pentru citire. Ciclul while
{ realizează citirea fişierului caracter cu caracter.
f=fopen("C:\\car.txt","r"); IniŃial, imediat după deschiderea fieşierului,
while (!feof(f)) pointerul (indicatorul) de fişier se poziŃionează
{ automat la începutul fişierului, înaintea primului
c=fgetc(f); caracter. Cât timp acest pointer nu a juns la sfârşitul
putchar(c); fişierului, adică atâta timp cât "not feof(f) este
}
}
adevărat" {while (!feof(f))}:
− citeşte un caracter din fişierul f în variabila c
{c=fgetc(f);};
− afişează caracterul citit {putchar(c);};
Dacă fişierul este cel din figură, în urma execuŃiei
programului se va reproduce "ad-literam" pe ecran
conŃinutul său. Astfel, se va afişa caracterele "&$?"
pe un rând, apoi caracterele "#*". ObservaŃi că
compilatorul "vede" sfârşitul unui rând şi reuşeşte
să treacă automat la rândul al doilea (ca şi cum ar
citi marcajul de sfârşit de rând ca pe un caracter).
char c;
FILE *f;
.............
fputc(f,’A’);
c=’B’;
fputc(f,c);
Ex e mp l u :
Fie fişierul text cu descriptorul f, având conŃinutul ca în 315∪-34.65
desenul alăturat (prin '∪' am simbolizat caracterul "spaŃiu ").
394
Presupunem că dorim să citim cele două numere din fişier, 315 şi -34.65, în
două variabile x şi y. Fireşte că cel mai indicat ar fi ca tipurile celor două variabile, pe care
le dăm la declarare, să coincidă cu tipurile valorilor pe care urmează să le găzduiască. Mai
concret, variabila x ar trebui să fie de tipul int, iar y de tipul float.
int x; float y;
InstrucŃiunea de citire este:
fscanf (f,"%d %f", &x, &y);
În urma execuŃiei instrucŃiunii vom avea x=315 şi y=-34,65.
În parametrul de control "%d %f", primul specificator de format "%d"
corespunde variabilei x de tipul int, iar al doilea specificator "%f" se asociază variabilei
y de tipul float. SpaŃiul prezent între cei doi specificatori în parametrul de control,
identifică tocmai spŃiul aflat între cele două valori în fişier.
Evident că tipurile variabilelor x şi y în care se citesc numerele din fişier nu
trebuie neapărat să fie identice cu tipurile valorilor citite, dar este necesar să fie
compatibile. Astfel, putem avea şi alte variante în privinŃa declarării celor două variabile,
dar trebuie să fim atenŃi să modificăm corespunzător şi specificatorii de format din
parametrii de control.
float x,y;
fscanf (f,"%f %f", &x, &y); // rezultă x←
← 315.0 şi y←
←-34.65
sau
int x,y;
fscanf (f,"%d %d", &x, &y); // rezultă x←
← 315 şi y←
← -34
Dacă de exemplu, cele două valori s-ar fi găsit în fişier una sub
alta ca în figura alăturată, atunci în parametrul de control al funcŃiei 315
-34.65
fscanf, între cei doi specificatori de format trebuia să punem marcatorul
de sfârşit de rând "\n", pentru a "comanda" saltul la rând nou în fişier
după citirea primului număr.
fscanf (f,"%d\n%f", &x, &y);
fprintf (<descriptor>,<param_control>,<var1>,<var2>,…);
Exemplu:
Să vedem ce valori va afişa programul următor, dacă fişierul f arată ca mai jos.
1 11 -4 12 8 -9 5
V I .1 . 5 . P r e l u c r a r e a f i ş i e r e l o r t e x t
c u ins tr ument e c ar e fol ose s c obiec t e
Ex e mp l u :
♥ fstream f("numere.txt",ios::in);
− deschide pentru citire fişierul numere.txt, cu descriptorul f, aflat în folder-ul curent;
♥ fstream g("numere2.txt,ios::out);
− deschide pentru scriere fişierul numere2.txt, cu descriptorul g, aflat în folder-ul curent;
♥ fstream f("D:\\Fisiere\\numere.txt",ios::in);
− deschide pentru citire fişierul numere.txt, cu descriptorul f, aflat pe partiŃia
"D" în folder-ul "Fisiere";
♥ fstream g("D:\\Fisiere\\numere2.txt",ios::out);
− deschide pentru scriere fişierul numere2.txt, cu descriptorul g, aflat pe
partiŃia "D" în folder-ul "Fisiere";
Ex e mp l u :
f.close();
Închide fişierul cu descriptorul f;
399
După cum aŃi observat, închiderea unui fişier se realizează la fel,
indiferent dacă a fost deschis pentru citire sau pentru scriere. PrezenŃa caracterului
"punct" între descriptor şi identificatorul close iarăşi nu poate fi explicată
momentan. Ea Ńine tot de teoria obiectelor, mai precis de modul în care este
referită funcŃia close ca membră a "clasei fişier" fstream.
400
Printr-o singură instrucŃiune, trebuie citite din fişier patru variabile, şi anume a, b,
c şi x. Ca să reuşească citirea, în fişier trebuie să existe patru valori, ale căror tipuri trebuie
să fie compatibile cu tipul variabilelor în care vrem să le citim. Dacă ne uităm cu atenŃie,
observăm potrivirea: în fişier avem trei valori întregi şi una reală, în această ordine, iar
dintre variabile, primele trei sunt de tipul int şi a patra de tipul float. Prin urmare, în
urma citirii, variabilelele vor primi valorile a=7, b=-11, c=3 şi x=9.25. Faptul că primele
trei valori sunt pe un rând iar a patra valoare pe al doilea rând nu afectează citirea, întrucât
marcajul de sfârşit de rând va fi "sărit" automat, la fel ca şi spaŃiile dintre numere.
Sintaxa: <descr> << val1 << val2 << ... << valn
2 $ 5
3.14 3.5
Sintaxa: <descr>.eof()
FuncŃia returnează 1 dacă s-a atins sfârşitul de fişier, respectiv 0 în caz contrar.
Exemplu:
Presupunem că avem un fişier cu descriptorul f, în care se găseşte un şir de numere
separate prin spaŃii, şi vrem să citim acest şir şi să-l afişăm pe ecran. Pentru aceasta, vom citi
numerele din fişier pe rând, în aceeaşi variabilă x, într-un ciclu care se va executa atâte timp
cât nu s-a atins sfârşitul de fişier, adică atâta timp cât funcŃia f.eof() nu returnează 1. La
fiecare pas, citim un număr din fişier în variabila x şi îl afişăm pe ecran.
while (!f.eof()) do
{
f >> x;
cout << x<
}
Rezolvare
:
Asignăm fişierului dat descriptorul f şi îl deschidem pentru citire, cu atributul
"r". Presupunând că fişierul a fost creat în directorul curent, instrucŃiunea de asignare şi
deschidere este {f=fopen("numere.txt","r");}. Citim valoarea lui n de pe primul
402
rând din fişier {fscanf(f,"%d\n",&n);}. IniŃializăm cu 0 suma S a elementelor
pozitive din şir.
Cele n elemente ale şirului vor fi citite succesiv din fişier în aceeaşi variabilă
x. Folosim un ciclu for, în care contorul i va număra elementele citite. Astfel, valorile lui
i vor fi pe rând 1,2,...,n, indicând la al câtâlea element al şirului am ajuns cu citirea
(pentru i=1 citim primul element, apoi pentru i=2 al doilea element, etc). La fiecare pas:
− citim în variabila x un element al şirului, aflat pe un rând în fişier
{fscanf(f,"%d/n ",&x);};
− testăm dacă elementul tocmai citit în x este pozitiv, iar în caz afirmativ îl
adăugăm la S {if (x>0) S+=x;}.
Programul se încheie cu închiderea fişierului şi afişarea lui S.
#include <iostream.h>
#include <stdio.h>
FILE *f;
int i,x,n,S;
void main ()
{
// asignare si deschidere fisier
f=fopen("numere.txt","r");
fscanf(f,"%d\n",&n); // citeste numarul de elemente de pe primul rand din fisier
S=0;
for (i=1; i<=n; i++) // citeste pe rand cele n elemente, din fisier in variabila x
{
fscanf(f,"%d/n",&x);
cout << x << " ";
// pentru fiecare x, daca este pozitiv, atunci il adauga la suma S a elementelor pozitive
if (x>0)
S+=x;
}
cout << "\nSuma elementelor pozitive este " << S;
}
ÎncercaŃi singuri !
►1. (Bacalaureat iulie 2009, varianta 1)
Fişierul text BAC.TXT conŃine, pe o singură linie, cel mult 1000 de
numere naturale nenule cu cel mult 4 cifre fiecare, numerele fiind separate prin
câte un spaŃiu. ScrieŃi un program care citeşte de la tastatură un număr natural
nenul n (n<=999) şi numerele din fişierul BAC.TXT şi care afişează pe ecran,
separate prin câte un spaŃiu, toate numerele din fişier care sunt divizibile cu n.
Dacă fişierul nu conŃine niciun astfel de număr, atunci se va afişa pe ecran mesajul
NU EXISTA.
Exemplu: dacă fişierul bac.txt conŃine numerele:
3 100 40 70 25 5 80 6 3798
atunci pentru n=10 pe ecran se va afişa:
100 40 70 80
403
►2. (Bacalaureat iulie 2001, varianta 1)
ScrieŃi un program Pascal care citeşte din fişierul text BAC.TXT 4000 de
numere naturale de cel mult trei cifre fiecare, apoi afişează pe ecran câte numere
pare şi câte numere impare există în fişier. Numerele sunt scrise pe prima linie a
fişierului, cu câte un spaŃiu între ele.
De exemplu, pentru fişierul BAC.TXT cu următorul conŃinut:
3 4 98 5 7 2 ... 2, unde 2 apare de 3995 ori, se vor afişa pe ecran
numerele 3997 şi 3.
►3. (Bacalaureat iulie 2009, varianta 42)
Fişierul text NUMERE.TXT conŃine pe prima linie un număr natural n
(0<n<100000) iar pe doua linie, separate prin câte un spaŃiu, n numere naturale
formate din cel mult două cifre.
a) ScrieŃi un program care determină în mod eficient, din punct de vedere
al timpului de executare, dacă numerele situate pe a doua linie a fişierului sunt în
ordine strict crescătoare. În caz afirmativ, programul va afişa pe ecran mesajul DA,
altfel va afişa mesajul NU.
Exemplu: dacă fişierul NUMERE.TXT are următorul conŃinut:
7
3 5 2 1 5 23 1
atunci pe ecran se va afişa: NU
b) DescrieŃi succint, în limbaj natural, metoda de rezolvare folosită,
explicând în ce constă eficienŃa ei (3 – 4 rânduri).
404
Rezolvare
:
Definim o variabilă nr, în care, la citirea fiecărui interval de forma [a,b],
memorăm numărul valorilor întregi din intervalul respectiv, care este b-a+1 (de exemplu,
intervalul [17,24] conŃine 24-17+1=8 valori întregi). Mai avem nevoie de încă două
variabile:
− nrmax = numărul maxim de valori intregi dintre toate intervalele [a,b], pe
care îl iniŃializăm cu -MAXINT (cel mai mic număr întreg posibil);
− cmin = cel mai mic dintre capetele din dreapta ale intervalelor care conŃin exact
nrmax valori intregi, pe care îl iniŃializăm cu MAXINT (cel mai mare întreg posibil).
Asignăm descriptorul f fişierului BAC.TXT şi îl deschidem pentru citire (cu
parametrul ios::in la funcŃia fstream). Apoi, de pe primul rând al fişierului, citim
valoarea lui n, reprezentând numărul de intervale {f >> n;}.
Ştiind că avem de citit din fişier n rânduri, proiectăm un ciclu for, în care
contorul i nu are alt rol decât acela de a număra citirile, parcurgând pe rând valorile
1,2,...,n. La fiecare pas trebuie să actualizăm, dacă este cazul, intervalul care
îndeplineşte ambele condiŃii din enunŃ, şi care trebuie afişat. Pentru aceasta, vom parcurge
în cadrul fiecărui pas următoarea succesiune de operaŃii:
Citim de pe un rând al fişierului, în variabilele a şi b, cele două numere care
reprezintă capetele unui interval de forma [a,b] {f >> a >> b;};
Calculăm numărul nr al valorilor întregi din intervalul [a,b] astfel format
{nr=b-a+1;};
Există două situaŃii în care se modifică intervalul cu număr maxim de valori
întregi şi capătul din dreapta minim, şi anume:
– dacă numărul valorilor întregi din intervalul [a,b] tocmai citit este mai
mare decât numărul maxim (nr>nrmax)
SAU
– dacă am dat de un interval [a,b] care conŃine un număr de valori
întregi întâlnit şi la alt interval, ŞI capătul din dreapta al intervalul este
mai mic decât capătul minim (b<cmin)
Într-un cuvânt, condiŃia care impune actualizarea intervalului [a,b] cerut
este: (nr>nrmax) || (nr==nrmax && b<cmin)
Dacă este îndeplinită condiŃia de mai sus, înseamnă că intervalul [a,b]
curent îndeplineşte ambele cerinŃe, deci:
– Noul capat minim cmin devine capatul b al intervalului curent
{cmin=b;};
– Noul nrmax devine numărul nr al valorilor întregi din intervalul
{nrmax=nr;};
– Salvăm capetele a şi b ale intervalului curent în variabilele cap1 şi
cap2, pentru că, cel puŃin pentru moment, acesta este intervalul selectat
ce trebuie afişat {cap1=a; cap2=b}.
for (i=1; i<=n; i++)
{
f >> a >> b;
cout << endl << "[" << a << "," << b << "]";
nr=b-a+1;
if ((nr>nrmax) || (nr==nrmax && b<cmin))
405
{
cmin=b;
nrmax=nr;
cap1=a;
cap2=b;
}
}
La finele ciclului, în variabila nrmax avem numărul maxim de valori întregi
dintre toate intervalele, iar în cap1 şi cap2 am salvat capetele intervalului cu nrmax valori
întregi, care în plus mai au şi capătul din dreapta minim. Ca atare, programul se încheie cu
afişarea acestor valori şi închiderea fişierului f.
void main ()
{
clrscr();
int n,i,a,b,nr,nrmax,cmin,cap1,cap2;
fstream f("bac.txt",ios::in);
f >> n; // citeste numarul n de intervale
nrmax=-MAXINT; // nrmax = numarul maxim de valori intregi aflate in intervalele
date
cmin=MAXINT;
// cmin = capatul din dreapta cel mai mic dintre interv. care contin nrmax valori intregi
cout << "Intervalele citite din fisier sunt: ";
// citeste cele n intervale, si, pentru fiecare interval, actualieaza nrmax si cmin
for (i=1; i<=n; i++)
{
f >> a >> b;
cout << endl << "[" << a << "," << b << "]";
nr=b-a+1;
if ((nr>nrmax) || (nr==nrmax && b<cmin))
{
cmin=b;
nrmax=nr;
// in cap1 si cap2 actualizam capetele intervalului care indeplineste cele doua conditii
cap1=a;
cap2=b;
}
}
cout << endl << "Intervalul este [" << cap1 << "," << cap2 <<
"]";
cout << endl << "Si contine " << nrmax << " valori intregi";
f.close();
getch();
}
406
ÎncercaŃi singuri !
Citim şirul de numere din fişier într-un vector v, folosind un ciclu while. La
fiecare pas citim un număr din fişier, adăugându-l ca element nou la sfârşitul vectorului.
Notăm cu i poziŃia la care am ajuns cu adăugarea în vector. Evident valoarea
iniŃială a lui i este 0, pentru că la început vectorul este gol şi primul element care se va
adăuga în el va ocupa poziŃia 0. Cât timp nu am ajuns la sfârşitul rândului în fişier {not
seekeoln(f)}:
citim un număr din fişier, în elementul v[i] al vectorului
{fscanf(f,"%d",&v[i]);}
"pregătim" poziŃia următoare la sfârşitul vectorului v, poziŃie pe care se va
memora numărul ce va fi citit la pasul următor al ciclului {i++;}
407
Valoarea finală a lui i reprezintă numărul de elemente ale vectorului {n=i;}.
i=0;
while (!feof(f))
{
fscanf(f,"%d",&v[i]);
cout << v[i] << endl;
i++;
}
n=i;
Exemplu:
Presupunem că fişierul conŃine şirul 12, -3, 8. IniŃial i=0.
Pasul 1: end of file ? nu ⇒ citeşte v[0]=12, i=i+1=0+1=1;
Pasul 2: end of file ? nu ⇒ citeşte v[1]=-3, i=i+1=1+1=2;
Pasul 3: end of file ? nu ⇒ citeşte v[2]=8, i=i+1=2+1=3;
Pasul 4: end of file? da ⇒ iese din ciclu.
n=i ⇒ n=3 numărul de elemente ale vectorului.
#include <iostream.h>
#include <stdio.h>
FILE *f,*g;
int n,i,j,temp,v[20];
408
void main ()
{
// citeste elementele vectorului din fisier, intr-un ciclu
f=fopen("C:\\BAC.TXT","r");
g=fopen("C:\\BAC2.TXT","w");
i=0;
while (!feof(f))
{
fscanf(f,"%d\n",&v[i]);
cout << v[i] << endl;
i++;
}
n=i;
// sorteaza crescator vectorul
for (i=0; i<=n-2; i++)
for (j=i+1; j<=n-1; j++)
if (v[j]<v[i])
{
temp=v[i];
v[i]=v[j];
v[j]=temp;
}
// scrie vectorul sortat in fisierul de iesire
for (i=0; i<=n-1; i++)
fprintf(g,"%4d",v[i]);
fclose(f);
fclose(g);
cout << "\nS-a scris vectorul sortat in fisierul vect.out";
}
ÎncercaŃi singuri !
409
AplicaŃie R.VI.4. Matrice în fişier text
Se citeşte o matrice din fişierul text "mat.in". Fişierul conŃine:
• pe primul rând numărul m de linii şi numărul n de coloane ale
matricii;
• pe fiecare din următoarele m rânduri, elementele unei linii a matricii,
separate prin spaŃii.
Să se interschimbe între ele două linii date L1, L2, scriindu-se matricea
rezultată în fişierul "mat.out" (elementele fiecărei linii a matricii pe un rând).
Exemplu: mat.in mat.out
4 3 1 5 9
L1=1, L2=2 1 5 9 3 7 11
2 6 10 2 6 10
3 7 11 4 8 12
Rezolvare 4 8 12
:
Citim matricea a cu m linii şi n coloane din fişierul f, deschis pentru citire. De
pe primul rând din fişier se citesc m şi n. Apoi, într-un ciclu, contorul i va lua pe rând
valorile 0,1,...,m-1 şi pentru fiecare i:
− citim linia i a matricii de pe un rând din fişier: într-un alt ciclu, parcurgem
coloanele j=0,1,...,n-1 ale liniei i şi citim din fişier fiecare element
a[i][j];
− trecem în fişier la rândul următor, cu fscanf(f,"\n").
ÎncercaŃi singuri !
411
AplicaŃie R.VI.5. Numere din fişier cu prima cifră pară
(Bacalaureat iulie 2008, varianta 88)
Fişierul DATE.IN conŃine cel mult 100000 de numere naturale, scrise
toate pe un rând şi separate prin spaŃii, fiecare număr având cel mult nouă cifre. Să
se realizeze un program care scrie în fişierul DATE.OUT, pe o singură linie,
separate prin câte un spaŃiu, toate numerele din fişierul DATE.IN care au prima
cifră pară. (prima cifră a unui număr este considerată cea mai semnificativă, adică
cea mai din stânga).
Exemplu:
dacă fişierul DATE.IN conŃine numerele
45 123 68 8 134 56 876 6666 2 5 123 65
atunci în fişierul DATE.OUT se vor găsi următoarele valori:
45 68 8 876 6666 2 65
Rezolvare
:
Mai întâi asignăm descriptorul f fişierului 'DATE.IN' şi îl deschidem pentru
citire cu parametrul "ios.in" {fstream f("date.in",ios::in);}, precum şi
descriptorul g fişierului 'DATE.OUT', pe care îl deschidem pentru scriere cu parametrul
"ios.out" {fstream g("date.out",ios::out);}.
412
(pentru că toate valorile care îndeplinesc condiŃia cerută trebuie scrise pe un
singur rând al fişierului de ieşire).
if (!(c%2))
g << x << " ";
void main ()
{
clrscr();
long x,d;
int c;
fstream f("f:\\Fisiere\\date.in",ios::in);
fstream g("f:\\Fisiere\\date.out",ios::out);
while (!f.eof()) // cat timp nu am ajuns la sfarsitul fisierului
{
// citeste un numar din fisierul f in variabila x
f >> x;
// extrage cifrele lui x
d=x;
while (d)
{
c=d%10;
d/=10;
}
// daca ultima cifra extrasa (prima din numar) este para, scrie numarul x in fisierul g
if (!(c%2))
g << x << " ";
}
cout << endl << "S-au scris numerele cerute in fisierul
date.out";
f.close();
g.close();
getch();
}
ÎncercaŃi singuri !
► 1. (Bacalaureat iunie 2008, varianta 72)
Pe prima linie a fişierului text BAC.IN se află un număr natural n
(0<n≤≤1000), iar pe a doua linie n numere reale pozitive, despărŃite prin câte un spaŃiu.
RealizaŃi un program care citeşte datele din fişierul de intrare, apoi scrie în fişierul
BAC.OUT, pe o singură linie, despărŃite prin câte un spaŃiu, acele numere din fişierul de
intrare a căror parte întreagă este număr prim. Dacă nu există nici o valoare în fişierul
BAC.IN cu proprietatea cerută, atunci se va afişa mesajul NU.
413
Exemplu: dacă fişierul BAC.IN are conŃinutul din figură,
6
12.095 31.567 5.789 789.834 1234.923 2.345
414
# include <stdio.h>
# include <iostream.h>
char c1,c2;
FILE *f,*g;
int e;
void main ()
{
f=fopen("c:\\1.txt","r");
f=fopen("c:\\2.txt","r");
e=1; // in final, e va fi 1 daca fisierele sunt identice, respectiv 0 in caz contrar
// citeste cate un caracter din cele doua fisiere si verifica egalitatea caracterelor
while (e && !(feof(f) || !feof(g)))
{
c1=fgetc(f);
c2=fgetc(g);
if (c1!=c2)
e=0;
}
// daca pana acum fisierele coincid, e posibil ca unul din ele sa aiba caractere in plus fata de celalalt
if (e)
if (feof(f) && !feof(g))
e=0;
if (!feof(f) && feof(g))
e=0;
fclose(f);
fclose(g);
if (e)
cout << "\n Fisiere identice";
else
cout << "\n Fisiere diferite";
}
ÎncercaŃi singuri !
► 1. (Bacalaureat iunie 2004, varianta 1)
ScrieŃi programul care creează fişierul text BAC.TXT ce conŃine pe prima
sa linie, în ordine, toate literele mari ale alfabetului englez aflate în alfabet după o
literă mare dată de la tastatură.
Exemplu: dacă se citeşte litera R, atunci BAC.TXT va conŃine: STUVWXYZ
415
(unde litera c apare la sfârşit de 981 de ori) iar de la tastatură se citeşte valoarea
n=11, atunci se va afişa pe ecran numărul 5, deoarece în şirul format din primele
11 caractere există cinci caractere cifră (cele subliniate).
►1. Care dintre următoarele operaŃii nu sunt posibile într-un fişier text ?
a) Modificarea unor valori în fişier, fără folosirea altor fişiere sau structuri de date.
b) Deschiderea pentru scriere.
c) Testarea sfârşitului de fişier.
d) Deschiderea pentru citire.
►2. Fie un fişier identificat prin descriptorul f şi deschis cu atributul "w". Fie de
asemenea două variabile întregi x şi y, ale căror valori sunt cunoscute. Care dintre
instrucŃiunile de mai jos pot fi executate astfel încât valorile celor două variabile să fie
scrise în fişier fiecare pe alt rând ?
a) fprintf(f,"%d\n%d",x,y);
b) fprintf("%d\n%d",x,y,f);
c) fprintf(f,"\n%d%d\n",x,y);
d) fprintf("\n%d%d\n",x,y,f);
►3. Câte numere se vor găsi în fişierul “nr.txt” după execuŃia programului următor ?
#include <stdio.h>
void main ()
{
int v[9]={0,1,0,0,2,3,0,4,5},i;
FILE *f;
f=fopen("nr.txt","w");
i=0;
while(i<9)
{
while(v[i])
{
fprintf(f,"%3d",v[i]); i++;
}
fprintf(f,"%3d",99);
i++;
}
fclose(f);
}
a) 4 b) 8 c) 9 d) 10
AbCdEfGhIj
416
Ştiind că în conformitate cu standardul ASCII literele mari au coduri succesive
începand cu 65 ('A'← ← 65, 'B'← ←66, etc.), iar literele mici au coduri succesive începand cu
97 ('a'←← 97, 'b'← ← 98, etc.), precizaŃi care va fi conŃinutul fişierului “car2.txt” după
execuŃia programului următor:
#include <stdio.h>
void main ()
{
char c1,c2;
FILE *f,*g;
f=fopen("car.txt","r");
g=fopen("car2.txt","w");
c1=96;
while (!feof(f))
{
c2=fgetc(f);
printf("%c",c2);
if (c2>c1) fputc(c2-32,g);
c1=c2;
}
fclose(f); fclose(g);
}
a) 1 b) 72 c) -72
d) Programul conŃine erori de sintaxă.
a) 0 b) 8 c) 20 d) 25
a) fseek(f,11,0); b) fseek(f,-2,2);
c) fseek(f,2,1); d) fseek(f,3,1);
418
Aprofundare
Probleme rezolvate
R.VI.7 El im in ă ri în tr - u n ş ir d i n f iş ier
(Bacalaureat iulie 2008, varianta 3)
ScrieŃi un program care citeşte din fişierul BAC.TXT un şir s de cel mult un
milion de numere naturale, fiecare număr având cel mult patru cifre, şi care
determină numărul de componente ale şirului obŃinut prin eliminarea din cele două
extremităŃi ale lui s a unui număr minim de componente, astfel încât şirul rezultat să
înceapă şi să se termine cu un număr par. Fişierul conŃine cel puŃin un număr par, iar
numerele din fişier sunt separate printr-un singur spaŃiu. Programul va afişa pe ecran
numărul de componente ale şirului obŃinut. Se cere un algoritm eficient din punctul
de vedere al timpului de executare şi al spaŃiului de memorie folosit.
Exemplu: dacă fişierul BAC.TXT conŃine numerele
1 245 22 67 34 29 345 8 354 11 7 34 12 45 39 41 26 67 89 1011
se va afişa pe ecran valoarea 15, deoarece sunt eliminate numerele
subliniate, iar şirul rămas este format din 15 numere.
Rezolvare
:
Întrucât şirul din fişier poate conŃine până la un milion de numere, nu este deloc
eficient să-l memorăm într-un vector. Nici subşirul cuprins între primul şi ultimul element
par nu trebuie salvat nicăieri, deoarece ne interesează numai lungimea acestuia, deci este
suficient să reŃinem doar poziŃiile capetelor sale. În concluzie, putenm citi valorile din şir
una după alta, în aceeaşi variabilă x.
Notăm cu p1 şi p2 poziŃia primului, respectiv ultimului element par (evident,
iniŃial, aceste variabile vor avea valoarea 0, iar în timpul citirii şirului se vor actualiza
corespunzător). În variabila k vom memora poziŃia din şir a numărului la care am ajuns cu
citirea în fişier (care la început va fi desigur tot 0). Declanşăm un ciclu care se va executa
atâta timp cât nu am ajuns la sfârşitul fişierului ("!f.eof()"), şi în care la fiecare pas:
citim un număr din fişier în variabila x;
incrementăm cu 1 valoarea lui k, pentru a reŃine în k poziŃia pe care o ocupă
numărul tocmai citit din fişier (astfel, plecând de la k=0, la citirea primului
număr avem k=1, apoi la a doua citire k=2, ş.a.m.d.);
dacă valoarea lui x este număr par ("!(x%2)"), actualizăm p1 sau p2, astfel:
– dacă variabila p1 are valoarea iniŃială 0, înseamnă că x-ul tocmai citit
este primul număr par din şir, deci poziŃia p1 a primului element par
este tocmai poziŃia k a acestui x (p1=k);
– în caz contrar, înseamnă că am trecut de primul număr par, şi valoarea x
tocmai citită este momentan ultimul element par, deci actualizăm
419
poziŃia p2 prin atribuirea similară p2=k (desigur, este posibil ca la pasii
următori să mai citim în x şi alte valori pare, caz în care p2 se va
modifica din nou);
După încheierea ciclului de citire a şirului, determinăm în variabila lung
lungimea subşirului alcătuit din primul şi ultimul element par, subşir delimitat de poziŃiile
p1 şi p2 (în felul acesta am simulat doar eliminarea din şir a elementelor aflate înaintea
primei valori pare şi a celor situate după ultima valoare pară, nefiind necesară ştergerea
propriuzisă a lor). Dacă în şir au fost găsite cel puŃin două valori pare, atunci p1 şi p2 vor
avea valori nenule, iar lungimea lung va fi |p2-p1+1| (într-adevăr, de exemplu, dacă
subşirul cuprins între prima şi ultima valoare pară are capetele date de p1=8 şi p2=13,
atunci el are este alcătuit din şase elemente, adică |13-8+1| elemente, cu poziŃiile
8,9,10,11,12,13). Numai că există şi două cazuri particulare, în care valoarea lui lung
nu se mai încadrează în formula de mai sus. Aşa de pildă, dacă există un singur element
par, atunci în final p1 va fi diferit de 0, şi p2 a rămas cu valoarea iniŃială 0, iar lungimea
subşirului va fi 1. De asemenea, dacă subşirul nu are nici un element par, atunci p1=p2=0
şi lung va fi 0. Înglobăm cazul general şi cele două cazuri particulare într-o instrucŃiune
"if-else" imbricată pentru a stabili valoarea lui lung, după care afişăm p1, p2 şi lung,
şi închidem fişierul.
void main ()
{
clrscr();
int x,p1,p2,k,lung;
fstream f("C:\\fisiere\\bac6.txt",ios::in);
p1=p2=k=0; // p1 si p2 = capetele subsirului cuprins intre primul si ultimul element
par
cout << "Sirul din fisier este:\n";
while (!f.eof()) // cat timp nu am ajuns la sfarsitul fisierului
{
f >> x; // citeste un numar din fisier in variabila x
k++; // k = pozitia pe care o ocupa numarul x in fisier
cout << x << " ";
// actualizeaza p1 daca x este primul numar par, respectiv p2 in caz contrar
if (!(x%2))
{
if (!p1)
p1=k;
else
p2=k;
}
}
// stabileste valoarea lui lung = lungimea subsirului dintre primul si ultimul element par
if (!p1 && !p2)
lung=0;
else
if (p1 && !p2)
420
lung=1;
else
lung=abs(p2-p1+1);
cout << endl << "Pozitiile capetelor sunt " << p1 << " si " <<
p2;
cout << endl << "Nr de componente ale subsirului cerut este " <<
lung;
f.close();
getch();
}
ObservaŃie:
Rezolvarea de mai sus este super-elegantă, pentru că reuşeşte să actualizeze atât
p1 cât şi p2 într-un singur ciclu. Desigur, se putea proiecta o soluŃie mai "băbească" în
care să tratăm separat cele două capete, în două cicluri, după cum urmează:
− într-un prim ciclu, citim numere din fişier succesiv, în variabila x, până la
întâlnirea primului element par, moment în care actualizăm p1 cu poziŃia k a numărului la
care am ajuns;
− într-un al doilea ciclu, citim tot succersiv restul şirului, până la întâlnirea
sfârşitului de fişier, şi actualizăm p2 de fiecare dată când întâlnim un număr par, ultima
valoare a lui p2 fiind cea care ne interesează.
Chiar dacă nu este atât de conchisă, şi această rezolvare e la fel de eficientă ca cea
dată de noi, pentru că la urma urmei tot o singură parcurgere a şirului are loc !
R.VI.8 Cel ma i ma r e nu mă r
(Bacalaureat iulie 2008, varianta 24)
Fişierul BAC.TXT conŃine pe prima linie un număr natural care poate
conŃine până la un milion de cifre din mulŃimea {0,1,2,...,9}. Cifrele
numărului nu sunt separate prin spaŃii. ScrieŃi un program care afişează cel mai
mare număr ce se poate obŃine din cifrele numărului aflat în fişier. Se va utiliza un
algoritm eficient din punctul de vedere al timpului de executare şi resurselor de
memorie folosite.
Exemplu: dacă fişierul conŃine numărul 24174800...01, unde cifra 0 se
repetă de 2000 de ori, atunci pe ecran se va afişa 874421100...0.
Rezolvare
Este
:
evident faptul că nu putem citi numărul din fişier într-o variabilă de tip
întreg, deoarece, potrivit enunŃului, acesta poate avea până la un milion de cifre. Nici
memorarea cifrelor numărului într-un vector de cifre nu este recomandabilă, deoarece
contravine cerinŃei privitoare la eficienŃa algoritmului.
Vom folosi un vector nr cu 10 elemente, în care poziŃiile i iau ca valori
elementele mulŃimii {0,1,2,...,...,9}, şi fiecare element nr[i] va stabili de câte ori
apare cifra respectivă i în cadrul numărului din fişier.
După asignarea descriptorului f pentru fişierul 'bac.txt' şi deschiderea
acestuia în vederea citirii (folosind funcŃia fstream), iniŃializăm cu 0 întreg vectorul nr.
Într-un ciclu, contorul i, va parcurge poziŃiile elementelor vectorului nr, de la 0 la 9, şi la
fiecare pas facem atribuirea nr[i]:=0.
421
Mai departe, trecem la citirea numărului din fişier, cifra cu cifra. Atâta timp cât
nu am ajuns la sfârşitul fişierului {while !f.eof()}, la fiecare pas:
− Citim o cifra din fişier, ca şi caracter, în variabila c {f >> c;};
− Afişăm cifra respectivă pe ecran, sub forma caracterului-cifră c;
− Incrementăm cu 1 acel element din vectorul nr care contorizează apariŃiile
respectivei cifre în fişier. Dar poziŃia unui element în vectorul nr este tocmai o cifră, iar
noi din fişier am citit în variabila c caracterul-cifră aferent, deci apare acum problema
trecerii de la caracterul-cifră la cifra aferentă. După cum ştiŃi, codurile ASCII ale
caracterelor-cifră '0','1','2',...,'9' sunt succesive, începând cu 48. Prin urmare,
diferenŃa dintre codul unui caracter-cifră oarecare c şi codul caracterului-cifră '0', este
chiar cifra respectivă. De exemplu; dacă am citit caracterul c='4', codul acestuia este 52,
codul cifrei '0' este 48, iar diferenŃa 52-48 este tocmai cifra 4. Aşadar, pentru a obŃine
cifra ca valoare numerică, este suficient să facem diferenŃa dintre codurile ASCII, care se
scrie sub forma c-'0'. Şi cum această cifră reprezintă poziŃia elementului ce trebuie
incrementat în vectorul nr, vom avea atribuirea {nr[c-'0']++}.
De pildă, pentru numărul 24174800...01 (în care cifra 0 se repetă de 2000 de
ori), în final vectorul nr va arăta astfel:
poziŃia '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
elementul 2000 2 1 0 2 0 0 1 1 0
Pentru a afişa cel mai mare număr care se poate forma cu cifrele din fişier, este
suficient să parcurgem vectorul nr în ordine inversă. Un contor i va parcurge poziŃiile de
la 9 până la 0, reprezentând cifrele zecimale, şi la fiecare pas vom scrie cifra i de atâtea ori
cât ne spune valoarea nr[i], cea care a stabilit numărul de apariŃii. Prin urmare, la fiecare
pas al acestui ciclu, pentru fiecare valoare a lui i, vom avea un alt ciclu, în care contorul j
va număra de la 1 la nr[i], şi pentru fiecare valoare a lui j afişăm cifra i (dacă de
exemplu, la un anumit pas al primului ciclu, pentru o anumită valoare a lui i, elementul
nr[i] a rămas cu valoarea iniŃială 0, adică cifra respectivă nu apare deloc în număr, atunci
în al doilea ciclu contorul j va evolua de la 1 la 0, deci al doilea ciclu nu se va executa
deloc şi, ca atare, cifra în cauză nu se va afişa).
Programul se încheie cu închiderea fişierului f.
void main ()
{
int i,j;
long nr[10];
char c;
clrscr();
fstream f("F:\\fisiere\\bac.txt",ios::in);
// initializeaza vectorul nr care va memora numarul de aparitii ale cifrelor
for (i=0; i<=9; i++)
nr[i]=0;
cout << "Numarul citit din fisier este " << endl;
// citeste numarul cifra cu cifra din fisier si actualizeaza vectorul nr
422
while (!f.eof())
{
f >> c;
cout << c;
nr[c-'0']++;
}
cout << endl << "Cel mai mare numar este: " << endl;
// afiseaza vectorul nr
for (i=9; i>=0; i--)
for (j=1; j<=nr[i]; j++)
cout << i;
f.close();
getch();
}
Rezolvare
:
Vom crea doi vectori, fie aceştia a şi b, unul pentru elementele pare, iar celălalt
pentru elementele impare ale şirului citit. Mai întâi citim toate numerele din fişier într-un
vector v, folosind un ciclu. Mai notăm cu n1 şi n2 numărul de elemente din vectorii a
respectiv b. IniŃializăm cu 0 un indice i. Cât timp nu am ajuns la sfârşitul fişierului de
intrare, identificat prin descriptorul f, citim un număr din fişier în elementul v[i] (cu
fscanf), apoi incrementăm contorul i. În felul acesta, numerele din fişier vor fi adăugate
succesiv la sfârşitul vectorului v, iniŃial gol. Valoarea finală a lui i reprezintă numărul de
elemente ale vectorului v, pe care îl memorăm în variabila n.
Într-un ciclu for, parcurgem pe rând elementele v[i] ale vectorului v, cu
i=0,1,...,n-1. Pentru fiecare element v[i], testăm dacă este par, şi:
În caz afirmativ, căutăm pe v[i] în vectorul elementelor pare a, proces pe
care îl vom controla cu ajutorul unei variabile "semafor" gata1, iniŃializată
cu 0 (cu sens de "false", adică "nu e gata"). În timpul acestei căutări,
parcurgem vectorul a deja existent cu un contor k1 (iniŃializat cu 0), şi
procedăm astfel:
• Comparăm pe v[i] cu fiecare element a[k1], atâta timp cât nu am ajuns
la sfârşitul vectorului a şi gata1 este 0. Dacă întâlnim un element a[k1]
egal cu v[i], înseamnă că elementul v[i] nu mai trebuie adăugat,
423
pentru că există deja în vectorul a, deci vom ieşi forŃat din ciclu prin
atribuirea gata1=1. Dacă am ajuns la un element a[k1] mai mare decât
v[i], atunci trebuie să inserăm pe v[i] în vectorul a, exact în locul în
care am ajus cu căutarea, adică pe poziŃia dată de valoarea la care a ajuns
contorul k1. Inserarea se face prin prin mutarea cu o poziŃie mai la
dreapta a tuturor elementelor care urmează după poziŃia cu pricina k1. În
sfârşit, dacă nu e nici un din cele două situaŃii anterioare (adică
a[k1<v[i]), trecem la următorul element, prin incrementarea lui k1;
• dacă la finele acestei căutări variabila gata1 a rămas cu valoarea 0,
înseamnă că elementul v[i] nu a fost găsit în vectorul a al elementelor
pare, şi trebuie să-l adăugăm la sfârşitul acestuia.
În caz contrar procedăm analog, dar cu vectorul b în locul vectorului a;
#include <iostream.h>
#include <stdio.h>
int v[30],a[30],b[30];
FILE *f,*g;
int i,k1,k2,n,n1,n2,j,gata1,gata2;
void main ()
{
// asignare si deschidere fisiere
f=fopen("c:\\sir.in","r");
g=fopen("c:\\sir2.out","w");
// citim numerele din fisier in vectorul v
i=0;
while (!feof(f))
{
fscanf(f,"%d",&v[i]);
i++;
}
n=i; // n =nr. elementelor citite
// afiseaza sirul citit din fisier
cout << n << endl ;
for (i=0; i<=n-1; i++)
cout << v[i] << " ";
n1=n2=0;
// n1 si n2 =numarul elementelor din vectorii a si b, unde a=vectorul numerelor pare,
// iar b=vectorul numerelor impare
for (i=0; i<=n-1; i++) // parcurge elementele v[i] ale vectorului v
{
if (!(v[i]%2))
{
// daca elementul v[i] este par, atunci il cauta in vectorul a
k1=0; gata1=0;
while (k1<=n1-1 && !gata1)
{
if (a[k1]==v[i])
gata1=1; // daca l-a gasit, iese fortat din ciclu
else
if (a[k1]>v[i])
// insereaza elementul in vectorul a pe pozitia necesara astfel incat
424
// vectorul sa ramana sortat crescator}
{
for (j=n1-1; j>=k1; j--)
a[j+1]=a[j];
a[k1]=v[i];
n1++;
gata1=1;
}
else
k1++;
} // de la while
// daca nu s-a gasit elementul v[i] in vectorul a, atunci il adauga la sfarsit
if (!gata1)
{
a[n1]=v[i];
n1++;
}
} // de la primul if din for
else
{
// daca elementul v[i], memorat in x, este impar, atunci il cauta in vectorul b
k2=0; gata2=0;
while (k2<=n2-1 && !gata2)
{
if (b[k2]==v[i])
gata2=1; // daca l-a gasit, iese fortat din ciclu
else
if (b[k2]>v[i])
// insereaza elementul in vectorul a pe pozitia necesara astfel incat
// vectorul sa ramana sortat crescator
{
for (j=n2-1; j>=k2; j--)
b[j+1]=b[j];
b[k2]=v[i];
n2++;
gata2=1;
}
else
k2++;
} // de la while
// daca nu s-a gasit elementul v[i] in vectorul b, il adauga la sfarsit
if (!gata2)
{
b[n2]=v[i]; n2++;
}
} // de la else
} // de la primul for
cout << endl << "S-a scris fisierul de iesire";
for (i=0; i<=n1-1; i++)
fprintf(g,"%d ",a[i]);
fprintf(g,"\n");
for (i=0; i<=n2-1; i++)
fprintf(g,"%d ",b[i]);
fclose(f); fclose(g);
}
425
R.VI.10 F iş ier c u n u me r e pr im e
ScrieŃi un program eficient care citeşte de la tastatură două numere
naturale n, m (5<n<m≤ ≤ 109 ) şi scrie în fişierul text "BAC.TXT" toate numerele
prime din intervalul închis [n,m] . Numerele se scriu în ordine crescătoare, câte
10 numere pe fiecare linie a fişierului, numerele dintr-o linie fiind despărŃite între
ele prin câte un spaŃiu. De exemplu, dacă se introduc de la tastatură valorile 87 şi
239 , atunci fişierul "BAC.TXT" va avea următorul conŃinut:
Rezolvare
:
Asignăm descriptorul g fişierului "bac.txt" şi deschidem fişierul pentru
scriere. Citim de la tastatură valorile lui n şi m. Într-un ciclu, variabila k va parcurge toate
numerele naturale de la n la m inclusiv. Pentru fiecare dintre aceste valori ale lui k:
Verificăm dacă numărul k este prim. IniŃializăm cu 1 o variabilă ok, de tip
semafor presupunând că numărul este prim (la final, valoarea lui ok, 0 sau 1
va indica "starea" condiŃiei testate). Apoi parcurgem într-un alt ciclu posibilii
divizori ai lui k, aceştia fiind j=2,3,...,[k/2]. La fiecare pas, dacă j
este divizor al lui k, variabila ok ia valoarea 0.
Dacă ok a rămas cu valoarea 1, înseamnă că numărul k este prim, deci acesta
trebuie scris în fişierul g. Dificultatea problemei constă în faptul că trebuie
scrise câte zece numere pe un rând. Pentru aceasta avem nevoie de o variabilă
p care în timpul parcurgerii intervalului (n,m) ne va indica la al câtâlea număr
am ajuns pe rândul curent în fişier. Înaintea ciclului iniŃializăm p=1, iar în
ciclu, înainte de scrierea unui număr prim k mai facem o testare:
dacă p<=10 vom scrie valoarea lui k pe rândul curent, acolo unde a
ajuns indicatorul de fişier;
în caz contrar, trecem pe rând nou în fişier şi "resetăm" contorul p prin
atribuirea p=1 (reluând astfel numărătoarea de la capăt), apoi scriem
valoarea lui k.
# include <iostream.h>
# include <stdio.h>
int p,k,j,m,n,ok;
FILE *g;
void main ()
{
g=fopen ("c:\\bac.txt","w");
cout << "n, m="; cin >> n >> m;
p=1; // variabila p va indica la al catalea nr suntem pe randul curent in fisier
for (k=n; k<=m; k++) // in variabila k trec pe rand numerele naturale de la n la m inclusiv
426
{
// testeaza daca numarul k este prim
ok=1;
for (j=2; j<=k/2; j++)
if (k%j==0) ok=0;
if (ok) // in cazul in care k este prim, il scrie in fisierul g
if (p<=10)
{
fprintf (g,"%4d",k); p++;
}
else
{
fprintf (g,"\n"); p=1;
fprintf (g,"%4d",k);
}
}
cout << "\nS-au scris numerele prime in fisier";
fclose(g);
}
Probleme propuse
Probleme suplimentare
► 1. (Bacalaureat iulie 2009, varianta 3)
Fişierului text NR.TXT conŃine pe o singură linie, separate prin câte un
singur spaŃiu, cel mult 100 de numere naturale, fiecare număr având cel mult 4
cifre. Să se scrie un program care citeşte toate numerele din fişier şi scrie pe ecran,
pe o singură linie, separate prin câte un spaŃiu, în ordine crescătoare, toate
numerele din fişier care au cel puŃin trei cifre. Dacă fişierul nu conŃine astfel de
numere se va afişa mesajul NU EXISTA.
Exemplu: dacă fişierul conŃine numerele 123 17 3844 459 9 68
atunci pe ecran se vor afişa valorile:
123 3844 459
429
► 12. (Bacalaureat iulie 2009, varianta 58)
Fişierul text BAC.TXT conŃine, pe o singură linie, cel puŃin două şi cel
mult 100 de numere naturale nenule distincte de cel mult patru cifre fiecare,
numerele fiind separate prin câte un spaŃiu. ScrieŃi un program care citeşte
numerele din fişier şi scrie pe ecran, în ordine crescătoare, cele mai mici două
numere dintre cele citite. Se cere un algoritm eficient de rezolvare.
Exemplu: dacă fişierul BAC.TXT conŃine numerele:
1017 48 310 5710 162
atunci se va afişa: 48 162
IndicaŃii: Un algoritm eficient trebuie să evite memorarea numerelor într-un
vector, motiv pentru care le vom citi pe rând într-un ciclu, în aceeaşi variabilă x, cât timp
nu am ajuns la sfâtşitul fişierului. Notăm cu min1 cel mai mic număr din fişier iar cu min2
al doilea număr ca mărime, şi iniŃializăm ambele variabile cu MAXINT. Pentru fiecare x
citit, facem două testări:
− dacă x este mai mic decât min1, atunci el devine noul min1;
− dacă x>min1 dar în acelaşi timp x<min2, atunci x devine noul min2.
► 13. (Bacalaureat iulie 2009, varianta 15)
În fişierul text BAC.IN se găsesc, pe o singură linie, separate prin câte un
spaŃiu, mai multe numere naturale de cel mult şase cifre fiecare. Se cere să se
determine şi să se afişeze pe ecran ultimul număr impar din fişierul BAC.IN. Dacă
în fişier nu există niciun număr impar se va scrie pe ecran mesajul Nu există
numere impare.
Exemplu: dacă fişierul BAC.IN conŃine valorile: 12 6 25 68 13 8
24 31 42 se va afişa 31.
a) DescrieŃi în limbaj natural un algoritm eficient, din punct de vedere al
spaŃiului de memorie şi al timpului de executare, pentru rezolvarea acestei
probleme, explicând în ce constă eficienŃa acestuia.
b) ScrieŃi programul corespunzător algoritmului descris.
► 14. (Bacalaureat iulie 2009, varianta 14)
Fişierul text BAC.TXT conŃine mai multe numere naturale cu cel mult şase
cifre fiecare, câte un număr pe fiecare linie a fişierului. ScrieŃi un program care
afişează pe ecran toate numerele din fişier, câte cinci numere pe fiecare linie,
separate prin câte un spaŃiu, cu excepŃia ultimei linii care poate conŃine mai puŃin
de 5 numere.
Exemplu: dacă fişierul conŃine numerele:
2 8 4 7 3 9 5 4 1 8 2 4 2 8 4 7 3
atunci pe ecran se vor afişa următoarele trei rânduri: 9 5 4 1 8
2 4
430
► 15. (Bacalaureat iulie 2009, varianta 80)
Fişierul text BAC.IN conŃine cel mult 1000 de numere naturale cu cel
mult patru cifre fiecare, despărŃite prin câte un spaŃiu. ScrieŃi un program care
citeşte numerele din fişier şi afişează pe ecran, în ordine crescătoare, acele numere
din fişier care au cifrele egale. Dacă fişierul nu conŃine niciun astfel de număr, se
va afişa pe ecran mesajul NU EXISTA.
Exemplu: dacă fişierul BAC.IN conŃine numerele: 30 44 111 7 25 5
atunci pe ecran se va afişa 5 7 44 111.
► 16. (Bacalaureat iulie 2009, varianta 47)
Fişierul text BAC.TXT conŃine pe prima linie un număr natural n
(n<100), iar pe a doua linie, separate prin câte un spaŃiu, n numere naturale, mai
mici decât 30000 fiecare. ScrieŃi un program care citeşte de la tastatură un număr
natural k (k<10) precum şi numerele din fişierul BAC.TXT apoi determină şi
afişează pe ecran, cu câte un spaŃiu între ele, toate numerele de pe a doua linie a
fişierului care conŃin în scrierea lor cifra memorată în variabila k. Dacă nu există
un asemenea număr pe a doua linie a fişierului, se afişează pe ecran mesajul NU.
Exemplu: dacă se citeşte de la tastatură k=2, iar fişierul are conŃinutul din
imagine, atunci pe ecran se afişează numerele: 234 202 427 92
8
234 5678 317 809 202 427 92 6004
OARECARE
ISOSCEL
ISOSCEL
NU E TRIUNGHI
ECHILATERAL
434
► 27. (Bacalaureat iulie 2009, varianta 31)
În fişierul NUMERE.TXT se află memorate, pe prima linie un număr natural
n (1≤n≤100), iar pe fiecare dintre următoarele n linii, câte două numere întregi
x,y (-100≤ ≤ x≤ ≤ 100 ), reprezentând capetele câte unui segment [x,y] desenat
≤ y≤
pe axa Ox a sistemului de coordinate carteziene.
a) ScrieŃi în limbajul C/C++ un program eficient din punct de vedere al
timpului de executare şi al spaŃiului de memorare, care citeşte din fişier datele
existente, determină segmentul rezultat în urma intersecŃiei tuturor celor n
segmente date şi afişează pe ecran două numere despărŃie printr-un spaŃiu ce
reprezintă capetele segmentului cerut. Dacă segmentele nu au niciun punct comun
se va afişa pe ecran valoarea 0. 5
b) DescrieŃi în limbaj natural algoritmul utilizat, justificând -7 10
eficienŃa acestuia. 3 20
Exemplu: dacă fişierul NUMERE.TXT are conŃinutul din -5 5
imagine, se va afişa pe ecran: 3 5 0 12
-8 30
435
► 30. În fişierul NR.TXT se găsesc n perechi de numere întregi.
1 3
Fişierul conŃine: pe primul rând valoarea lui n, apoi, pe fiecare din 1 2
următoarele n rânduri, elementele unei perechi separate printr-un spaŃiu. 2 2
Să se afişeze numărul perechilor cu proprietatea că media aritmetică a 3 9
celor două numere din pereche este egală cu o valoare dată M (unde M se -2 6
citeşte de la tastatură). 0 1
Exemplu: pentru M=2 şi fişierul din figură, programul va afişa
valoarea 3.
► 31. În fişierul SIR.IN se găseşte un şir de numere întregi, scrise toate
pe un rând, separate prin câte un spaŃiu. Nu se cunoaşte numărul de elemente ale
şirului. RealizaŃi un program care inserează între oricare două elemente ale şirului
media lor aritmetică. Elementele şirului rezultat după inserare, se vor scrie în
fişierul SIR.OUT, unul sub altul (câte unul pe fiecare rând).
IndicaŃii: A insera câte o valoare între oricare două elemente, înseamnă a face
o inserare înaintea fiecărui element începând cu al doilea până la ultimul. Altfel spus,
într-un ciclu, la fiecare pas:
− inserăm valoarea m=(v[i-1]+v[i])/2, înaintea elementului v[i], adică pe
poziŃia i; în acest scop, vom muta cu o poziŃie mai la dreapta toate elementele de la v[i]
inclusiv până la sfârşitul vectorului, adică într-un alt ciclu cu j de la i la n-1, facem
v[j+1]=v[j]. Apoi, aducem valoarea dorită m pe poziŃia i, prin simpla atribuire v[i]=m.
− facem i=i+1, pentru a merge la următorul element al vectorului iniŃial, adică
pentru a trece peste valoarea tocmai adăugată în vector.
► 32. (Bacalaureat iulie 2005, enunŃ modificat)
Fişierul text BAC.TXT conŃine pe prima sa linie un şir de maxim 1000 de
caractere, care pot fi litere cifre şi spaŃii. RealizaŃi programul Pascal care afişează
pe ecran câte caractere cifră există între al p-ulea şi al q-ulea caracter din fişier
(unde valorile poziŃiilor p şi q se citesc de la tastatură).
Exemplu: dacă p=2, q=8, iar fişierul BAC.TXT conŃine succesiunea de
caractere mx12a3pb55...5, unde cifra 5 se repetă de 961 ori, atunci programul va
afişa valoarea 3, deoarece între poziŃiile 2 şi 8 din fişier se găsesc trei caractere cifră.
► 33. (Bacalaureat iulie 2009, varianta 31)
Se citeşte de pe prima linie a fişierului text NUMERE.IN un număr natural
n (0<n<10000) iar de pe a doua linie a fişierului n numere naturale din
intervalul [1,100] şi se cere să se afişeze pe ecran, despărŃite prin câte un spaŃiu,
numărul sau numerele întregi din intervalul [1,100] care nu apar printre
numerele citite. Dacă pe a doua linie a fişierului apar toate numerele din intervalul
precizat, se va afişa mesajul NU LIPSESTE NICIUN NUMAR. AlegeŃi un algoritm
de rezolvare eficient din punctul de vedere al timpului de executare.
Exemplu: pentru fişierul NUMERE.IN cu următorul conŃinut:
12
4 2 3 1 6 5 7 8 9 11 10 100
► 34. Se dau două fişiere text. Primul dintre acestea, numit LITERE.TXT,
conŃine o mulŃime de litere mari distincte ale alfabetului englez, scrise pe un
singur rând, despărŃite prin câte un spaŃiu. Al doilea fişier, numit FRAZA.TXT
conŃine mai multe rânduri de text, scris numai cu litere mari, rânduri de lungimi
diferite. Nu se cunoaşte nici numărul caracterelor din primul fişier, nici numărul
rândurilor din cel de-al doilea. RealizaŃi un program care stabileşte câte din literele
existente în fişierul LITERE.TXT apar în fişierul FRAZA.TXT de cel puŃin p ori
(unde valoarea lui p se citeşte de la tastatură).
Exemplu: pentru p=4 şi fişierele:
FRAZA.TXT LITERE.TXT
MIE IMI PLACE MULT M I U S
INFORMATICA
438
► 39. (Bacalaureat iulie 2009, varianta 87)
Fişierul text BAC.TXT conŃine un şir de cel mult 2008 numere naturale
nenule, cu cel mult 4 cifre fiecare, pe mai multe rânduri, numerele de pe acelaşi
rând fiind separate prin câte un spaŃiu.
a) ScrieŃi un program care citeşte de la tastatură un număr natural k şi
afişează pe ecran cel mai mare număr din fişierul BAC.TXT care este mai mic sau
egal cu numărul natural k, precum şi numărul de apariŃii ale acestuia în fişier,
folosind o metodă eficientă din punctul de vedere al timpului de executare. Cele
două valori vor fi afişate pe o linie a ecranului, separate printr-un spaŃiu. Dacă în
fişier nu există nici un număr mai mic sau egal cu k, se va afişa doar valoarea 0.
Exemplu: dacă în fişier avem numerele 31 2 63 71 8 63 5 281 şi
numărul citit este k=70, atunci pe ecran se vor afişa numerele: 63 2.
b) DescrieŃi succint, în limbaj natural, algoritmul utilizat, justificând
eficienŃa acestuia.
► 40. Se dă un fişier text care conŃine mai multe şiruri de numere întregi,
fiecare şir fiind scris pe câte o linie a fişierului (cu elementele separate prin spaŃii).
Nu se cunoaşte nici numărul şirurilor şi nici câte elemente conŃine fiecare şir.
AfişaŃi pe ecran numerele de ordine ale liniilor din fişier care conŃin şiruri
ordonate crescător.
Exemplu: pentru fişierul din figură se vor afişa valorile 1, 4 şi 5 deoarece
primul, al patrulea şi al cincilea rând din fişier conŃin şiruri ordonate crescător.
sir1.txt
1 3 5 7
8 7 5
12 14 15 10
33 66
0 1 2
Probleme de nota 10
► 41. (Bacalaureat iulie 2009, varianta 90)
Fişierul text BAC.TXT conŃine pe mai multe rânduri cel mult 50000 de
numere naturale, numerele aflate pe acelaşi rând fiind separate prin câte un spaŃiu.
Fiecare număr are cel mult patru cifre.
a) ScrieŃi un program care, utilizând un algoritm eficient din punct de
vedere al timpului de executare şi al spaŃiului de memorie folosit, determină
numărul din fişier care are cei mai mulŃi divizori. În cazul în care există mai multe
valori în fişier care au număr maxim de divizori, programul va afişa cea mai mică
dintre acestea.
Exemplu: dacă fişierul conŃine valorile 23 12 100 36 atunci se va afişa 36,
pentru că atât 100 cât şi 36 au număr maxim de divizori, dar 36 este cel mai mic.
b) DescrieŃi succint, în limbaj natural, algoritmul utilizat, justificând
eficienŃa acestuia.
439
► 42. Fişierul NR1.TXT conŃine:
− pe primul rând k numere naturale, n1, n2,...,nk, separate prin spaŃii;
− pe fiecare din următoarele k rânduri câte un şir de numere întregi
separate prin spaŃii: şirul de pe al doilea rând are n1 elemente, cel de pe al treilea
rând n2 elemente, etc., şirul de pe utlimul rând având nk elemente.
RealizaŃi un program care construieşte fişierul NR2.TXT care va conŃine
elementele maxime ale şirurilor aflate în fişierul NR1.TXT începând cu al doilea
rând. Aceste maxime se vor scrie în fişierul de ieşire toate pe un singur rând
separate printr-un spaŃiu.
Exemplu:
NR1.TXT NR2.TXT
3 6 5 8 11 8 4 12
2 11 -6
4 -2 8 -9 -5 0
0 1 2 3 4
12 3 9 -6 4 11 -8 1
A.TXT
atunci programul va afişa valoarea 4, deoarece 41111, 11111, 51111,
31111 sunt mai mici decât toate elementele din fişierul B.TXT.
b) DescrieŃi succint, în limbaj natural, metoda utilizată la punctul a),
justificând eficienŃa acesteia.
► 54. (Bacalaureat iulie 2000, varianta 3)
Pe prima linie a fişierului text BAC2000.TXT se găseşte o succesiune de
cel puŃin două şi cel mult 2000 de caractere, caractere ce pot fi doar litere mari şi
spaŃii. ScrieŃi un program Pascal care citeşte de la tastatură un număr natural k
(0<k<1000) şi stabileşte dacă există în fişier vreo literă care apare de exact k ori.
Programul care afişează pe ecran mesajul "Da" în cazul în care există cel puŃin o
literă cu proprietatea menŃionată şi mesajul "Nu", în caz contrar.
Exemplu: dacă fişierul BAC2000.TXT are următorul conŃinut
EXAMEN DE BACALAUREAT LA INFORMATICA
iar de la tastatură se citeşte numărul 8, atunci, deoarece litera A apare de
exact 8 ori, se va afişa pe ecran mesajul "Da".
► 55. (Bacalaureat iulie 2009, varianta 48)
Fişierul text BAC.IN conŃine pe prima linie un număr natural n
(0<n<5000), iar pe a doua linie, separate prin câte un spaŃiu, n numere naturale,
formate din cel mult patru cifre fiecare. ScrieŃi un program care determină şi scrie
în fişierul BAC.OUT toate numerele conŃinute de a doua linie a fişierului care apar
444
o singură dată în această linie. Numerele determinate se vor afişa în ordinea
crescătoare a valorilor lor, separate prin câte un spaŃiu.
Exemplu: dacă pe prima linie a fişierului BAC.IN se află 10, iar pe linia a
doua se găsesc numerele 2 4548 568 4548 57 89 5974 2 89 32 atunci
valorile căutate sunt 32 57 568 5974.
► 56. (Bacalaureat iulie 2009, varianta 33)
În fişierul NUMERE.TXT se află memorate pe prima linie două numere
naturale n şi m (având cel mult patru cifre fiecare, cu m≤n ), iar pe următoarea
linie, în ordine strict crescătoare, n numere naturale.
a) ScrieŃi în limbajul un algoritm eficient din punct de vedere al gestionării
memoriei şi timpului de executare, care citeşte din fişier datele existente şi
afişează cea mai mare sumă a m numere aflate pe a doua linie a fişierului.
b) ExplicaŃi în limbaj natural metoda utilizată, justificând eficienŃa acesteia.
► 57. (Bacalaureat iulie 2009, varianta 64)
Fişierul text DATE.IN conŃine pe prima linie, separate prin câte un spaŃiu,
cel mult 1000 de numere naturale, fiecare dintre ele având maximum 9 cifre.
a) ScrieŃi un program care citeşte numerele din fişierul DATE.IN,
determină şi afişează pe ecran numărul de elemente ale celei mai lungi secvenŃe
ordonate strict descrescător, formate din valori citite consecutiv din fişier. AlegeŃi
o metodă de rezolvare eficientă din punctul de vedere al timpului de executare.
Exemplu: dacă fişierul DATE.IN conŃine: 5 2 9 4 3 6 3 2 1 0 8
pe ecran se afişează valoarea 5, reprezentând lungimea secvenŃei (6 3 2 1 0)
b) DescrieŃi succint, în limbaj natural, metoda de rezolvare folosită,
explicând în ce constă eficienŃa ei (3-4 rânduri).
► 58. (Bacalaureat iulie 2008, varianta 93)
Se citeşte de la tastatură un număr natural n de cel mult 8 cifre. Să se
creeze fişierul text NR.TXT care să conŃină, câte unul pe linie, în orice ordine, toate
numerele naturale distincte care se pot obŃine din valoarea lui n prin eliminarea
uneia sau mai multor cifre de la unul din capetele sale.
Exemplu: pentru n=38604, fişierul NR.TXT va conŃine, câte unul pe linie
şi nu neapărat în această ordine: 8604 604 4 3860 386 38 3
► 59. Se dă fişierul text FR.IN care conŃine pe fiecare linie câte două
numere naturale mai mici decât 35000, reprezentând numărătorul respectiv
numitorul unei fracŃii simple.
a) Să se creeze fişierul FR.OUT care va conŃine pe fiecare rând numărătorii şi
numitorii fracŃiilor ireductibile rezultate prin simplificarea fracŃiilor din fişierul 'fr.in'.
b) Pe ultima linie a fişierului FR.OUT să se scrie numărătorul şi numitorul
fracŃiei rezultate prin adunarea fracŃiilor din fişierul FR.IN. FracŃia sumă se va da
în formă ireductibilă.
Exemplu: 1 3
2 6
pentru 'fr.in' 12 18 fişierul 'fr.out' va conŃine: 2 3
5 2
50 20
7 2
445
►60. Un institut de cercetări militare, care efectuează studii ultrasecrete în
domeniul armelor bilogice, a descoperit existenŃa unei scurgeri de informaŃii către
exterior. Pentru a pune capăt acestei situaŃii, conducerea institutului a decis
introducerea unui nou algoritm de codificare a cartelelor magnetice care permit
accesul în încăperile institutului. Codul PIN al fiecărei cartele va fi generat
pornind de la un fişier PIN.TXT alcătuit din m rânduri de numere naturale, fiecare
rând conŃinând n numere separate prin spaŃii. Pentru construirea acestui cod
trebuie procedat astfel:
− se citesc succesiv numerele din fişier, de pe fiecare rând de la stânga la
dreapta iar rândurile de sus în jos, rezultând astfel un şir de m*n numere naturale;
− se împarte şirul în subşiruri de câte p elemente consecutive;
− pentru fiecare astfel de subşir, se încearcă formarea unui număr natural,
luând câte o cifră din fiecare element al subşirului, astfel încât numărul rezultat să
aibă aspect de deal, adică să aibă cifrele în ordine strict crescătoare; dacă există un
astfel de număr, el va constitui un aşa numit cod intermediar; în cazul în care sunt
posibile mai multe soluŃii, se va reŃine ca şi co intermediar primul număr construit.
− dintre codurile intermediare rezultate prin "cercetarea" tutror subşirurilor
de câte p elemente, se va alege acela care este maxim; el va reprezenta codul PIN
al unei cartele.
Din motive de securitate, fiecare angajat primeşte propriul fişier de m*n
numere PIN.TXT şi trebuie să-şi găseascp singur codul PIN al cartelei care îi va
permite să pătrundă în incinta institutului. RealizaŃi programul care implementează
algoritmul de formare a unui cod PIN.
Exemplu:
Fie fişierul PIN.TXT cu conŃinutul de mai jos, în care m=3 (numărul
liniilor) şi n=4 (numărul elementelor de pe fiecare rând). Fie de asemenea p=3.
447
448
449
450