Tiedostojen käsittely¶
Vertaillaan tiedostojen käsittelyä Pythonilla ja C++:lla. Seuraavassa on (kummallakin kielellä kirjoitettuna) yksinkertainen ohjelma, joka laskee tiedostossa olevien kokonaislukujen summan. Lukujen pitää olla tiedostossa jokaisen omalla rivillään. Tyhjiä rivejä ei saa olla.
def main():
tiedoston_nimi = input("Syötä tiedoston nimi: ")
try:
tiedosto_olio = open(tiedoston_nimi, "r")
summa = 0
for rivi in tiedosto_olio:
summa += int(rivi)
tiedosto_olio.close()
print("Lukujen summa: ", summa)
except IOError:
print("Virhe tiedoston avaamisessa.")
except ValueError:
print("Virhe tiedoston rivillä")
main()
#include <iostream>
#include <fstream> // Huomaa kirjasto
#include <string>
using namespace std;
int main() {
string tiedoston_nimi = "";
cout << "Syota tiedoston nimi: ";
getline(cin, tiedoston_nimi);
// Yritetään avata tiedosto
ifstream tiedosto_olio(tiedoston_nimi);
if ( not tiedosto_olio ) {
cout << "Virhe tiedoston avaamisessa." << endl;
return 0;
}
// Tiedoston avaaminen onnistui
int summa = 0;
string rivi;
while ( getline(tiedosto_olio, rivi) ) {
summa += stoi(rivi);
}
tiedosto_olio.close();
cout << "Lukujen summa: " << summa << endl;
}
Itse asiassa ohjelmat eivät toimi täysin samoin, koska C++-koodissa
tiedostosta luettu rivi muutetaan kokonaisluvuksi stoi-funktiolla,
joka ei huomaa, jos rivin lopussa on jotain ylimääräistä.
Mikäli rivin alussa on jotain kokonaisluvuksi kelpaamatonta, stoi
aiheuttaa poikkeuksen, ja ohjelman suoritus päättyy.
Poikkeusten käsittelyyn palaamme myöhemmillä ohjelmointikursseilla.
Jos C++:ssa halutaan lukea tiedostoja, toimitaan seuraavasti:
Otetaan ohjelman alussa käyttöön
fstream-kirjasto, joka sisältää tiedostonkäsittelyssä käytettäviä tietotyyppejä.Riippuen siitä, halutaanko tiedostoa lukea vai kirjoittaa, määritellään joko
ifstream- taiofstream-tyyppinen olio, jonka rakentajalle annetaan parametrina käsiteltävän tiedoston nimi.Tiedoston avaamisen epäonnistuminen voidaan tunnistaa käyttämällä tiedostomuuttujaa
if-rakenteen ehtona: sille evaluoituu arvotrue, jos tiedoston avaaminen onnistui,falsemuussa tapauksessa.Kuten Pythonissakin, helpoin tapa lukea tiedostoa on suorittaa lukeminen silmukassa rivi kerrallaan merkkijonomuuttujaan, jota sitten jatkokäsitellään merkkijono-operaatioilla.
Kun tiedosto on luettu tai kirjoitettu loppuun, on hyvä tapa sulkea se
close-metodia kutsumalla. Esim. tiedostoja kirjoitettaessa tämä varmistaa, että kaikki kirjoitettavat tiedot tulevat talletetuiksi kovalevylle. Lisäksiclose-metodi vapauttaa tiedoston ohjelman käytöstä.
Tiedostomuuttujasta, joka on tyypiltään ifstream, voidaan lukea tietoa myös
lukuoperaattorin >> avulla samalla tavalla kuin on totuttu lukemaan
tietovirtamuuttujasta cin.
Tiedostoon kirjoittaminen (siis kovalevylle tallentaminen) tapahtuu
kohdistamalla ofstream-tyyppiseen tiedostomuuttujaan
tulostusoperaattori << aivan kuten on totuttu tekemään cout:in kanssa.
Yleisnimitys muuttujalle, jolla voidaan käsitellä (lukea ja kirjoittaa) tietokoneen oheislaitteita, on tietovirta (stream). C++:ssa kaikki oheislaitteiden käsittely on toteutettu tietovirtojen avulla. Lisäksi toteutusmekanismi on niin elegantti, että kaikkia syötevirtoja voidaan käsitellä toistensa kanssa identtisesti. Sama pätee myös tulostusvirroille.
Konkreettisesti tämä tarkoittaa sitä, että sekä cin:iä että
kaikkia ifstream-tyyppisiä tietovirtamuuttujia käsitellään
samoilla operaatioilla ja ne käyttäytyvät yhdenmukaisesti. Toisaalta
myös sekä cout:ia että ofstream-tyyppisiä virtoja käsitellään
samoin. Tämä on hyvä asia, koska ohjelmoijan ei tarvitse opetella kuin
yksi mekanismi, jota sitten voidaan soveltaa useaan
käyttötarkoitukseen.
Tulostus- ja syöteoperaatioita¶
Lyhyt lista hyödyllisiä operaatioita tietovirtojen käsittelyyn:
cout << tulostettavatulostusvirta << tulostettavaTulostusvirtaan saadaan tulostettua tai tallennettua tietoa tulostusoperaattorin
<<avulla.cin >> tallennusmuuttujasyötevirta >> tallennusmuuttujaSyötevirrasta voidaan lukea tunnetun tyyppinen tietoalkio suoraan kyseistä tyyppiä olevaan muuttujaan lukuoperaattorin
>>avulla.getline(syötevirta, rivi)getline(syotevirta, rivi, erotinmerkki)Luetaan syotevirrasta rivillinen tekstiä ja talletetaan se merkkijonomuuttujaan rivi. Jos kutsussa annetaan kolmas parametri -
char-tyyppinen erotinmerkki, lukemista ja rivi-muuttujaan tallentamista jatketaan, kunnes tietovirrassa tulee vastaan ensimmäinen erotinmerkki.string tekstirivi = ""; // Yksi rivillinen näppäimistöltä getline(cin, tekstirivi); // Seuraavaan kaksoispisteeseen saakka // (saattaa lukea useita rivejä kerralla) getline(tiedosto_olio, tekstirivi, ':');
syötevirta.get(merkki)Luetaan syötevirrasta yksi merkki (
char) muuttujaan merkki:// Tiedoston voi lukea läpi merkki kerrallaan: char luettu_merkki; while ( tietosto_olio.get(luettu_merkki) ) { cout << "Merkki: " << luettu_merkki << endl; }
Tietovirtaoperaatioiden onnistumisen tarkastaminen¶
Tietovirtamuuttujaa voidaan C++:ssa käyttää if-rakenteen ehtona.
Tällöin kääntäjä tulkitsee tietovirtaa bool-tyyppisenä (suorittaa
implisiittisen tyyppimuunnoksen) siten, että arvona on true, jos
tietovirta on kunnossa ja false, jos ei. Esimerkiksi:
ifstream tiedosto_olio("tiedosto.txt");
if ( not tiedosto_olio ) {
// Avaaminen epäonnistui, tietovirta on epäkunnossa
cout << "Virhe! ..." << endl;
return ...;
}
// Avaaminen onnistui, tehdään jotain tiedostolle
...
tiedosto_olio.close();
Kaikkien tietovirtoihin kohdistuvien operaatioiden onnistumista voi
tarkastella C++:ssa kirjoittamalla operaation vaikkapa if- tai
while-rakenteen ehdoksi. Esimerkiksi:
while( getline(tiedosto_olio, rivi) ) {
// Toistorakenteeseen mennään, jos tietovirrasta saatiin luettua rivi
}
// Toistorakenne päättyy, kun rivejä ei enää saada luettua,
// eli kun tiedosto on luettu loppuun
Tarkemmin sanottuna tässä siis tapahtuu seuraavaa.
Aina, kun suoritat
ohjelmakoodissasi jonkin tietovirtaan kohdistuvan operaation, tulee
operaation paluuarvoksi se tietovirta, johon operaatio kohdistui. Jos
operaatio on esim. if- tai while-rakenteen ehtona, suorittaa
kääntäjä tietovirralle implisiittisen tyyppimuunnoksen
bool-tyyppiseksi. Kuten edellä selitettiin, arvoksi tulee true
tai false riippuen siitä, onko viimeisin kyseiseen tietovirtaan
kohdistunut operaatio on onnistunut.
Tämän opintojakson tehtävät saa yksinkertaisimmin ratkaistua siten,
että kirjoittaa tietovirtoihin kohdistuneet operaatiot aina e.m. rakenteiden
(if, while) ehdoksi, jolloin tietovirtaoperaation
onnistuminen tulee testattua.