You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
8. DUOMENŲ MAINAI SU FAILAIS<br />
Čia pateikiamos svarbiausios žinios apie tai, kaip programa turėtų nuskaityti duomenis iš<br />
pradinių duomenų failo(-ų) ir/ar įrašyti gautus rezu<strong>lt</strong>atus į failą(-us) išorinėje kompiuterio<br />
atmintyje. Tai aktualu, kai pradinių duomenų rinkinys programai yra nemažas (pavyzdžiui,<br />
vienmatis ar dvimatis masyvas) – derinant ir keletą kartų leidžiant programą tokiu atveju nereiks<br />
kiekvieną kartą kartoti duomenų įvedimo iš klaviatūros, o užteks tuos duomenis vienąkart įrašyti<br />
kaip pradinį failą į ilgalaikę kompiuterio atmintį. Dėl šios priežasties šis <strong>skyrius</strong> pateikiamas<br />
būtent šioje knygos vietoje, nors jo medžiagai suprasti reikia žinių apie klases ir jų metodus.<br />
Panašiai mes jau elgėmės: be platesnių komentarų savo programose naudojome klasių istream<br />
objektus cin ir ostream – cout. Todėl pirmąkart skaitant knygos medžiagą galima apsiriboti tik 8.1<br />
skyreliu, o vėliau, įsisavinus žinias apie klases, metodus ir paveldimumą – vėl sugrįžti prie šio<br />
skyriaus ir išnagrinėti likusią jo dalį.<br />
8.1 ĮVADINĖS ŽINIOS<br />
Kad įvestis iš failo ar išvestis į failą būtų panašios įvesčiai/išvesčiai iš klaviatūros / į ekraną, yra<br />
sukurta programinė srauto abstrakcija. Tai – simbolių (arba kitokios rūšies duomenų) seka,<br />
nukreipiama į kurį nors kompiuterio įrenginį arba tiesiog išorinės atminties failą. Srautą reikia<br />
susieti su tuo norimu failu. Šiame etape failui suteikiamas vardas, kuriuo į jį kreipsis programa, ir<br />
susiejamas su to failo vardu išorinėje atmintyje (t.y. vardu, kurį „žino“ operacinė sistema). Šis<br />
etapas dažniausiai vadinamas „failo atvėrimu“. Dabar galima atlikti įvesties/išvesties operacijas, o<br />
jas baigus – pageidautina failą „uždaryti“. Vėliau galima tą patį failą susieti su kitu programos vardu<br />
ir kitokiomis charakteristikomis, ir t.t. Tai – visas darbo su failu ciklas.<br />
Programiškai srautas yra tam tikrų standartinių C++ klasių objektas, o visi veiksmai su juo<br />
atliekami reikiamais klasių metodais. Įvesties iš failo klasė yra ifstream, išvesties – ofstream; darbui<br />
su jomis reikia antraštinio failo . Pirmiausia programoje reikia sukurti šių klasių objektus<br />
teikiant jiems norimus vardus. Sakykim, ifstream objektui suteiksime vardą is, o ofstream – os.<br />
Šiuos programos vardus turime susieti su failų operacinės sistemos vardais. Tarkime, į išorinę<br />
kompiuterio atmintį, į vadinamąjį darbinį katalogą, kur yra programos projekto failas *.vcproj, jau<br />
turime įrašę duomenų failą vardu duomenys.txt. Norime, kad programa sukurtų rezu<strong>lt</strong>atų failą<br />
tame pat kataloge vardu rezu<strong>lt</strong>atai.txt. Tada programoje failus atversime taip:<br />
#include <br />
. . .<br />
ifstream is;<br />
ofstream os;<br />
is.open(“duomenys.txt“);<br />
os.open(“rezu<strong>lt</strong>atai.txt“);<br />
. . .<br />
Jei failai yra (ar kuriami) kitame kataloge, reikia nurodyti pilnąjį failo vardą su visu keliu iki to<br />
katalogo nuo, pavyzdžiui, šakninio katalogo C:. Jei duomenų failo nurodytu adresu nebus – kils<br />
vykdymo meto klaida. Jei rezu<strong>lt</strong>atų failo nebus – failas bus sukurtas; jei rezu<strong>lt</strong>atų failas tokiu vardu<br />
jau buvo – jis bus perrašytas.<br />
Atvėrus failus, įvestis/išvestis atliekama lygiai taip pat, kaip dirbant su objektais cin ir cout:<br />
operacijomis >> ir
Baigus darbą su failais, rekomenduojama juos „uždaryti“. Jei programa baigia darbą<br />
korektiškai, nebus bėdos failų ir neuždarius: jie bus užverti automatiškai. Kitais atvejais failai gali<br />
būti sugadinti. Failai užveriami taip:<br />
. . .<br />
is.close( );<br />
os.close( );<br />
. . .<br />
1 pavyzdys. Programa turi nuskaityti du sveikuosius skaičius iš jau sukurto duomenų failo<br />
duomenys.txt, esančio darbiniame projekto kataloge, gauti jų sumą ir pradinius duomenis bei<br />
rezu<strong>lt</strong>atą išvesti į failą rezu<strong>lt</strong>atai.txt tame pat kataloge. Prieš pradedant pirmąkart programai darbą<br />
rezu<strong>lt</strong>atų failo dar nėra.<br />
#include <br />
using namespace std;<br />
//<br />
int main( ) {<br />
ifstream is; // failų skelbimai<br />
ofstream os;<br />
//<br />
is.open( "duomenys.txt" ); // failų atvėrimas<br />
os.open( "rezu<strong>lt</strong>atai.txt" );<br />
//<br />
int s1, s2;<br />
is>>s1>>s2; // darbas su failais<br />
os
Press any key to continue . . .<br />
Kaip matyti iš programos teksto, įvedant duomenis iš failo nebereikia jokio dialogo su<br />
programos vartotoju, tačiau duomenys pradiniame faile turi būti parengti pagal tokį šabloną, kokio<br />
reikalauja įvedimo operatorių tvarka. Duomenys faile turi būti skiriami vienas nuo kito tarpu(-ais)<br />
arba Enter simboliais. Savo programose vis tik dubliuosime duomenų ir rezu<strong>lt</strong>atų spausdinimą ne<br />
tik į failus, bet ir į pu<strong>lt</strong>o ekraną – bent jau iškart matysite, ką veikia programa.<br />
8.2 SRAUTŲ KLASĖS IR JŲ PAGRINDINIAI METODAI<br />
Pagrindinė, bazinė srautų klasė yra ios, turinti daugybę įvesties/išvesties metodų ir konstantų,<br />
pavyzdžiui, formatavimo vėliavėlių. Visus šiuos klasės narius paveldi įvesties srauto klasė istream ir<br />
išvesties srauto – ostream. istream klasėje yra metodai get( ), getline( ), read( ) ir perkraunama<br />
operacija >>, o ostream klasėje – put( ), write( ) ir perkraunama operacija
ofstream( “rezu<strong>lt</strong>atai.txt“, ios::out | ios::truc );<br />
Dažna programos vykdymo laiko klaida – nepavyksta atverti failo, pavyzdžiui, nerandamas<br />
programai reikalingas duomenų failas. Yra metodas fail( ), kuri grąžina reikšmę true, jei failo<br />
nepavyko atverti, ir false – jei pavyko. Todėl programoje reikėtų tikrinti bent jau duomenų failų<br />
atvėrimą (juk jei bus nerasti rezu<strong>lt</strong>atų failai – jie automatiškai pagal numatymą sukuriami):<br />
ifstream is;<br />
is.open( “duomenys.txt“ );<br />
if( is.fail( ) ){<br />
cout
}<br />
ifstream is;<br />
ofstream os;<br />
//<br />
coutfvd;<br />
coutfvr;<br />
//<br />
is.open( fvd );<br />
if( is.fail( ) ) {<br />
cout
s= S<br />
i= 123<br />
d= 1.999<br />
e1= Eilute1<br />
e2= ir<br />
Kaip matyti, iš visi vieno failo paimti duomenys, išskyrus eilutinius, sėkmingai perke<strong>lt</strong>i į kitą<br />
failą. Atrodytų, e1 reikšmė turėtų būti Eilute1 ir, o e2 – Eilute2. Rezu<strong>lt</strong>atai tokie gauti todėl, kad<br />
operacija >> eilutinius duomenis skaito iki pirmojo intervalo simbolio; po intervalo esantis žodis ir<br />
nuskaitomas jau kita operacija į e2. Duomuo Eilute2 duomenų faile liko išvis nenuskaitytas. Kaip<br />
korektiškai skaityti eilutinius duomenis su intervalais ir Enter simboliais – kitame skyriuje.<br />
8.3 SRAUTŲ KLASĖS istream IR ostream<br />
Kaip minėta šios klasės yra mums jau pažįstamų klasių ifstream ir ofstream protėviai ir skirtos<br />
atitinkamai duomenims iš srauto skaityti ir rašyti į srautą. Jose yra tokie pagrindiniai metodai.<br />
istream pagrindiniai metodai:<br />
>> – funkcija visiems standartiniams duomenų formatams. Iš srauto paima vieną duomenį į<br />
dešinėje ženklo >> pusėje nurodytą ląstelę<br />
get( s ) – iš srauto paima vieną char formato simbolį į ląstelę s. Grąžina nuorodą į srautą<br />
get( sm ) – iš srauto paima visus char formato simbolius iki eilutės pabaigos požymio \n ir<br />
įtalpina į vienmatį char formato masyvą sm. Grąžina nuorodą į srautą<br />
get( sm, n ) – veikia kaip get( sm ), tik paima iš srauto n-1 simbolį arba visus simbolius iki \n<br />
get( sm, sk ) – veikia kaip get( sm, n ), tik paima iš srauto simbolius iki skyriklio simbolio sk<br />
get( sm, n, sk ) – veikia kaip get( sm, sk ), tik paima iš srauto arba visus simbolius iki sk – jeigu<br />
jų skaičius neviršija n-1, arba n-1 simbolį<br />
getline( sm, n, sk ) – tas pat, kaip get( sm, n, sk ), tik skyriklis sk išmetamas iš srauto<br />
putback( s ) – įterpia paskutinį iš srauto paimtą char simbolį atgal į srautą<br />
ignore( n, sk ) – iš srauto išmeta n arba visus iki skyriklio sk (su skyrikliu kartu) esančius srauto<br />
duomenis<br />
peek( s ) – nuskaito vieną char simbolį, bet jį palieka sraute<br />
read( sm, n ) – veikia kaip get( sm, n ), tik paima iš srauto n-1 simbolį arba visus simbolius iki<br />
failo pabaigos simbolio EOF<br />
gcount( ) – grąžina simbolių, kuriuos nuskaitė funkcijos get, getline arba read, skaičių<br />
3 pavyzdys: faile duomenys.txt yra tekstas, Suskaičiuosime, kiek tame tekste yra simbolių A ir<br />
kiek – a. Rezu<strong>lt</strong>atus išvesime į ekraną.<br />
#include <br />
#include <br />
#include <br />
using namespace std;<br />
//<br />
int main( ) {<br />
84
}<br />
//<br />
char s; // simbolis, kurį nuskaitysime iš failo<br />
int kiekA = 0, kieka = 0; // A ir a kiekiai<br />
//<br />
ifstream is( "duomenys.txt" );<br />
if( is.fail( ) ) {<br />
cout s ) { // a<strong>lt</strong>ernatyva<br />
if( s == 'A' ) kiekA++;<br />
if( s == 'a' ) kieka++;<br />
}<br />
//<br />
cout
write( sm, n ) – iš char formato masyvo sm n simbolių įrašo į srautą<br />
4 pavyzdys. Programoje suformuotas vienmatis simbolinis masyvas. Vėliau metodu write 10<br />
šio masyvo elementų nukopijuojama į srautą os, kuris nukreipiamas į diskinį failą rezu<strong>lt</strong>atai.txt.<br />
#include <br />
#include <br />
#include <br />
using namespace std;<br />
//<br />
int main( ) {<br />
//<br />
char sm[ ] = "Pradinis duomenu srautas";<br />
//<br />
ofstream os( "rezu<strong>lt</strong>atai.txt" );<br />
if( os.fail( ) ) {<br />
cout
showpoint – realiems duomenims visada rodyti dešimtainį tašką<br />
showpos – prieš teigiamus aritmetinius duomenis rodyti „+“<br />
scientific – aritmetinius duomenis išvesti eksponentine forma<br />
fixed – tas pat, su fiksuota kablelio vieta<br />
Pagrindiniai manipuliatoriai be argumentų...:<br />
ws – įvesties metu ignoruoti intervalo simbolius<br />
dec – išvesti dešimtaine forma<br />
oct – išvesti aštuntaine forma<br />
hex – išvesti šešioliktaine forma<br />
endl – įterpti į išvesties srautą naujos eilutės simbolį ‘\n‘ ir ištrinti išvesties buferį<br />
ends – įterpti į išvesties srautą eilutės pabaigos simbolį ‘\0‘<br />
flush – ištrinti išvesties buferį<br />
...su argumentais:<br />
setw( int n ) – išvedamam simboliui nustatyti n pozicijų išvesties lauko plotį<br />
setprecision( int n ) – išvedant realių aritmetinių duomenų trupmeninėje dalyje palikti n<br />
skaitmenų<br />
setfill( int s ) – nustato simbolį, kuriais bus užpildomi tarpai tarp duomenų išvestyje.<br />
Numatytoji reikšmė – intervalas<br />
setiosflags( flags ) – pake<strong>lt</strong>i nurodytas formatavimo vėliavėles (jei vėliavėlių kelios – tarp jų<br />
įrašomas loginės sudėties ženklas |)<br />
resetiosflags( flags ) – nuleisti nurodytas formatavimo vėliavėles (jei vėliavėlių kelios – tarp jų<br />
įrašomas loginės sudėties ženklas |)<br />
Pagrindiniai ios metodai:<br />
char fill( ) – grąžina tarpų tarp duomenų užpildymo simbolį<br />
fill( char s ) – nustato tarpų tarp duomenų užpildymo simbolį s<br />
int precision( ) – grąžina realių aritmetinių duomenų trupmeninės dalies skaitmenų skaičių<br />
precision( int ) – tas pat, tik nustato tą tikslumą<br />
int width( ) – grąžina nustatytą išvesties lauko plotį pozicijomis<br />
width( int ) – tas pat, tik nustato tą plotį<br />
setf( flags ) – pakelia nurodytas vėliavėles (jei vėliavėlių kelios – tarp jų įrašomas loginės<br />
sudėties ženklas |)<br />
unsetf( flags ) – nuleidžia nurodytas vėliavėles (jei vėliavėlių kelios – tarp jų įrašomas loginės<br />
sudėties ženklas |)<br />
setf( flags, set ) – iš pradžių nuleidžia visas vėliavėlių rinkinio set vėliavėles, po to – pakelia<br />
vėliavėles flags. Rinkinių duomenys, kaip ir vėliavėlės, yra sąrašo enum tipo. Pavyzdžiui, rinkinio<br />
reikšmė adjustfield apima visas vėliavėles tekstui išvesties lauke lygiuoti.<br />
Kaip pastebėjote iš manipuliatorių ir metodų sąrašų, formatus galima nustatyti keliais būdais:<br />
srauto objektui kviesti reikiamą metodą, arba į srautą įterpti atitinkamą manipuliatorių su<br />
argumentais arba be jų. Pavyzdžiui, norėdami du aritmetinius duomenis išvesti į ekraną tik<br />
eksponentine forma, galėtume tą padaryt taip:<br />
. . . . . .<br />
double x, y; double x, y;<br />
87
. . . . . .<br />
cout.setf( ios::scientific );<br />
cout
while( !is.eof( ) ){<br />
is>>s;<br />
if( s == ‘A‘ ) kiekA++;<br />
if( s== ‘a‘ ) kieka++;<br />
}<br />
. . .<br />
Iliustruodami klaidų metodus, pateiksim pavyzdį-schemą programos fragmento, leidžiančio<br />
visiškai patikimai įvesti bet kokio formato (fragmente – int) aritmetinį duomenį. Tarkim, kad<br />
duomenys turi būti įvedami kiekvienas atskiroje eilutėje. Vienos įvesties operacijos patikra galėtų<br />
būti:<br />
. . .<br />
int i;<br />
. . .<br />
while( true ) {<br />
couti;<br />
if( cin.good( ) ) break;<br />
cout
#include <br />
#include <br />
#include <br />
#include <br />
using namespace std;<br />
//<br />
void iofile( ifstream&, ofstream&, int precision, int width );<br />
//<br />
int main( ) {<br />
//<br />
ifstream is("duomenys.txt");<br />
ofstream os("rezu<strong>lt</strong>atai.txt");<br />
if( is.fail( ) ) {<br />
cout
}<br />
}<br />
exit( -3 );<br />
}<br />
os