24.03.2015 Views

8 skyrius - techmat.vgtu.lt

8 skyrius - techmat.vgtu.lt

8 skyrius - techmat.vgtu.lt

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!