Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Tiedostojen lukeminen

sane [12.10.2006 16:10:55]

#

Hei!

Olen melko aloittelija c++ kanssa vielä, eli ihan perushommat vaan onnistuu.. Tarkoitus olisi saada luettua tiedostoa rivi kerrallaan, tai tiedosto taulukkoon tyyliin php, ja jokainen rivi löytyisi sit omasta taulustaan. Tiedosto on muotoa

1:5
2:3
3:4
...

Eli onko tällaiseen olemassa valmista funktiota?

Heikki [12.10.2006 16:32:47]

#

Tiedoston lukeminen rivi kerrallaan:

#include <fstream>
  std::ifstream filu("foo.dat");   // Avataan tiedosto
  while(!filu.eof()) {             // Niin kauan kun ei olla lopussa
    std::string rivi;
    getline(filu, rivi);           // Luetaan rivi
    // Nyt sitten tehdään tuolle riville mitä halutaan
  }
  filu.close();                    // Suljetaan tiedosto

sane [12.10.2006 16:35:08]

#

Kiitoksia nopeasta vastauksesta:)

Edit: Kun yritän tulostaa tuon rivin sisällön lauseella

cout << filu << endl;

tulee tulokseksi joka riville sama "0x22fea4", eli missä mättää?

Metabolix [12.10.2006 16:53:27]

#

Ei tiedostoa niin käytetä. Rivi pitää lukea tiedostosta muuttujaan. Et voi tulostaa tietovirtaoliota, koska se ei ole itse tietoa vaan vain olio, joka lukee tietoa tiedostosta, kun sen käskee lukea.

Vaihtoehtosi ovat C:n stdio.h-otsikon lukufunktiot, joista kerrotaan myös Putkan C-opassarjassa, ja C++:n tietovirrat, jotka tapaavat olla omalla tavallaan kömpelöitä ja usein myös hitaita suurten tietomäärien kanssa. Taulukon joudut joko varaamaan itse dynaamisesti, mikä ei aloittelijalle ole kovin helppo tapa. Helpompana vaihtoehtona on C++:n vector-luokka.

Jotenkin tähän malliin siis esimerkiksi:

// C++:n lukufunktiot ja vektoriluokka
#include <fstream>
#include <vector>
#include <limits>

// Ohjelma käyttää näitä luokkia
using std::ifstream;
using std::vector;
using std::ws;
using std::numeric_limits;

// Tiedoston sisältö tallennetaan tällaisiin tietueisiin
struct t_lukupari {
    int a, b;
};

// Vektori (eräänlainen taulukko), joka sisältää tietueitamme
vector<t_lukupari> lukuparit;

// Lukufunktio
void lue_tiedosto(char * tiedostonimi)
{
    // Apumuuttuja
    t_lukupari pari;

    // Tietovirta ja sen avaus
    ifstream virta(tiedostonimi);

    // Luetaan loppuun asti
    while (!virta.eof()) {
        try {
             virta >> pari.a; // Ensimmäinen luku
             virta.ignore(1); // Välimerkki
             virta >> pari.b; // Toinen luku
             virta >> ws;     // Tyhjät pois
             lukuparit.push_back(pari); // Lisätään vektoriin
        } catch (...) {
             // Virhe lukemisessa, skipataan rivin loppuun asti eli niin monta merkkiä kuin suinkin on ennen rivinvaihtoa (\n)
             virta.ignore(numeric_limits<int>::max(), '\n');
        }
    }
}

Tämä menetelmä on kuitenkin virhealtis, koska esimerkiksi rivi, jolla on vain yksi luku, sotkee kaiken. C:n funktioista on esimerkki Putkan C-oppaassa. Kuvaamasi tiedostomuodon lukemisessa auttaa fscanf-funktio muotoilulla "%i:%i".

koo [12.10.2006 20:22:05]

#

Minen kyllä ihan allekirjoita väitettä C++-streamien kömpelyydestä ja hitaudesta. Streameja voi muokata ja laajentaa melkein miten vaan. Ovat ne lähtökohtaisesti vähän C:n värkkejä hitaampia, kun ne on pakko toteuttaa C-kirjastofunktioiden päälle, mutta pienellä tuumaamisella ja kääntäjän optimointivipujen käytöllä nopeusero on usein varsin siedettävä. Ja muutoin streamit ovat kyllä sitten turvallisempia käyttää.

IO-hommat ovat ihan aikuisten oikeesti vaikeita. Yksi ongelma on siinä, kun yrittää miettiä, millaisista virhetilanteista vielä yritetään toipua ja miten. Jos esimerkiksi tässä tapauksessa jokin rivi on virheellinen, voidaanko mihinkään muuhunkaan luettuun tai vielä lukemattomaan dataan enää luottaa? Jos ei älyä, että virhe on yleensä virhe, tulee usein rakennelleeksi aivan liian hienoa ja itsessään virheellistä muka-toipumislogiikkaa.

Kun vielä ottaa huomioon, ettei streameissa ole virheraportointi exceptioneilla oletusarvoisesti päällä, olisin mukiloimassa edellisen esimerkin tähän malliin:

#include <fstream>
#include <vector>

struct lukupari
{
        int a, b;
        lukupari(int a_, int b_) : a(a_), b(b_) {}
};

std::vector<lukupari> lue_parit(char const *tiedostonimi)
{
        std::ifstream tiedosto(tiedostonimi);
        std::noskipws(tiedosto);
        int a, b;
        char sep, lf;
        std::vector<lukupari> taulukko;
        while ((tiedosto >> a >> sep >> b >> lf) && sep == ':' && lf == '\n')
                taulukko.push_back(lukupari(a, b));
        if (!tiedosto.eof())
                throw "tiedostonluvussa meni jotakin vikaan\n";
        return taulukko;
}

Toivottavasti tuosta saisi tarpeeksi vinkkejä lukea manuaaleja ja kirjoja oikeasta kohdasta, vaikkei C++:n kanssa niin pitkällä vielä olisikaan.

FrozenFire [21.10.2006 20:15:09]

#

Näitä kysellään verraten säännöllisesti... Voisi olla paikka vinkille/oppaalle. Jos jollakin osaavalla olisi aikaa.

(Itse osaan kyllä käyttää mutta ei ole kykyjä neuvoa, ja aikakin on usein kortilla)

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta