/* VaihdaTabt5a.cpp - vaihtaa tabulaattorit välilyönnekisi Alkuperäinen versio ilman metodien toteutusta ohjelmointikurssin 2002 välikokeen liite. Vesa Lappalainen 17.3.2002 Mallivastaukseksi tehtävään 5 metodit täydentänyt Jani Korhonen 25.3.2002 Täydennykset on kommentoitu koodiin. Muutoksia on tehty metodeihin tulosta ja tulosta_rivi. Tulosta on laitettu ensin tutkimaan koko tiedostosta onko siellä sellaisia sarakkeita, jotka ovat vain numerotietoa sisältäviä (poislukien otsikkosarake). Tulosta-funktio välittää sitten tiedon sarakkeiden laadusta tulosta_rivi -funktiolle, joka osaa tasaa kunkin rivin "sanan" sen mukaan, kuuluuko tuo "sana" vain numeroita sisältävään sarakkeeseen vai tekstiä sisältävään sarakkeeseen. (Jos "puhdas" numerosarake, niin tasaus oikeaan laitaan, muuten vasempaan). Otsikkorivit tulosta_rivi tasaa aina vasempaan laitaan. Voisi olla myös niin, että otsikkorivitkin tasataan yo. ehdon mukaan. Tämä on makuasia eikä vaikuta tehtävän pisteytykseen. */ #include #include #include #include #include "mjonotpp.h" #include "streampre.h" #include "dosout.h" using std::ostream; using std::ifstream; using std::string; using std::setw; using std::ios; using std::endl; // lisätty, koska tarvitaan rivinvaihtoa tulostuksessa using std::left; // lisätty, koska tarvitsee muuttaa tasausta sen mukaan, onko using std::right; // tulostettava pala numero vai ei const MAX_TABS = 50; class cTabs { unsigned int tabs[ MAX_TABS ]; public: cTabs( int deftab = 0 ) { for ( int i = 0; i < MAX_TABS; i++ ) // silmukassa MAX_TABS kertaa tabs[i] = deftab; // sijoitetaan oletusarvo kuhunkin taulukon alkioon } void lisaa( int i, const string &sana ) { if ( i < 0 ) return; // varmistetaan, että indeksi on if ( i > MAX_TABS - 1 ) return; // rajoissa unsigned int pituus = sana.length(); // otetaan sanan pituus talteen if ( pituus > tabs[i] ) // jos pituus on suurempi kuin taulukon vanha arvo tabs[i] = pituus; // niin sijoitetaan se entisen arvon tilalle } unsigned int tab( int i ) { if ( i < 0 ) return 0; // varmistetaan, että indeksi on if ( i > MAX_TABS - 1 ) return 0; // rajoissa return tabs[i]; // ja palautetaan taulukon alkio } }; class cVaihda { cTabs tabs; char erotin; string tiedosto; void tutki_rivi( const string &rivi ); void tulosta_rivi( ostream &os, const string &s, bool onko_numero[ MAX_TABS ], bool otsikko = false ) const; public: cVaihda( char iErotin, int minLev ) : tabs( minLev ), erotin( iErotin ) { } void tutki( const string &tied_nimi ); void tulosta( ostream &os ) const; }; void cVaihda::tutki_rivi( const string &s ) { int i = 0; // rivin "sanojen" laskuri string pala; // rivin "sana" = erotin-merkillä erotettu osa riviä string rivi = s; // lokaali kopio parametrista, koska erota-funktio ei // hyväksy const-merkkijonoa while ( rivi != "" ) { // jatketaan niin kauan, kun rivi:ssä on tavaraa pala = erota( rivi, erotin ); // erotetaan pala tabs.lisaa( i, pala ); // lisätään tarvittaessa palan pituus tabs:iin i++; // siirrytään seuraavaan palaan -> indeksi kasvaa } } void cVaihda::tutki( const string &tied_nimi ) { string rivi; // tiedoston rivi tiedosto = tied_nimi; // asetetaan tiedoston nimi attribuuttiin ifstream f( tiedosto.c_str() ); // avataan tiedosto. Huom! ifstreamin // konstruktori haluaa C-merkkijonon if ( !f ) { // tarkistetaan onnistuiko tiedoston avaus cout << "Tiedosto ei aukea!" << endl; return; } while ( getline( f, rivi ) ) // luetaan tiedostoa kunnes rivit loppuvat tutki_rivi( rivi ); // tutkitaan rivin "sanojen" pituudet f.close(); // Suljetaan tiedosto. Tässä tarpeeton, sillä f olio sulkee // tiedoston destruktorissaan. } void cVaihda::tulosta_rivi( ostream &os, const string &s, bool onko_numero[ MAX_TABS ], bool otsikko ) const { int i = 0; // rivin "sanojen" laskuri string pala; // rivin "sana" = erotin-merkillä erotettu osa riviä string rivi = s; // lokaali kopio parametrista, koska erota-funktio ei // hyväksy const-merkkijonoa while ( rivi != "" ) { // jatketaan niin kauan, kun rivi:ssä erotettavaa pala = erota( rivi, erotin ); // erotetaan pala // jos ei olla otsikkorivillä, niin sitten saatetaan joutua muuttamaan // sisennystä sen mukaan, onko ko. sarake vain numeroista koostuva vai ei if ( !otsikko ) { if ( onko_numero[ i ] ) os << right; else os << left; } os << setw( tabs.tab( i ) ) << pala << " "; // tulostus os:iin i++; // siirrytään seuraavaan palaan -> indeksi kasvaa } } void cVaihda::tulosta( ostream &os ) const { int i; bool onko_numero[ MAX_TABS ]; string rivi; // tiedoston rivi string pala; ifstream f( tiedosto.c_str() ); // avataan tiedosto. Huom! ifstreamin // konstruktori haluaa C-merkkijonon if ( !f ) { // tarkistetaan onnistuiko tiedoston avaus cout << "Tiedosto ei aukea!" << endl; return; } // tutkitaan ensin, mitkä sarakkeet koostuvat pelkistä numeroista // (tämä olisi voinut olla oma metodinsa) // alustetaan onko_numero[] -taulukko true-arvoilla for ( i = 0; i < MAX_TABS; i++ ) onko_numero[i] = true; // seuraavaksi luetaan tiedostoa rivi kerrallaan ja tutkitaan onko // jokin rivin "sanoista" muu kuin numero (tämä olisi voinut olla // myös oma aliohjelmansa) getline ( f, rivi ); // ohitetaan ensimmäinen rivi (=otsikkorivi) while ( getline( f, rivi ) ) { // luetaan tiedostoa kunnes rivit loppuvat pala = erota( rivi, erotin ); // otetaan ensimmäinen "sana" rivi:stä i = 0; // alustetaan indeksi nollaksi aina uuden rivin alkaessa while ( pala != "" ) { // jatketaan niin kauan, kun rivi:ssä erotettavaa // jos löytyy yksikin sellainen pala, mikä ei ole luku, niin kyseisen // sarakkeen kohdalle laitetaan arvo false if ( sallituissa( pala, "1234567890-." ) >= 0 ) onko_numero[i] = false; i++; pala = erota( rivi, erotin ); // otetaan seuraava pala } } // palataan tiedoston alkuun sulkemalla ja avaamalla se heti uudestaan f.close(); f.open( tiedosto.c_str() ); if ( !f ) { // tarkistetaan onnistuiko tiedoston avaus cout << "Tiedosto ei aukea!" << endl; return; } os << left; // varmistetaan tasaus vasempaan laitaan (kuten yleensä oletus) // tulostetaan ensin otsikkorivi if ( getline( f, rivi ) ) { // ensimmäinen rivi tiedostosta tulosta_rivi( os, rivi, onko_numero, true ); // tulostetaan otsikkorivinä os << endl; // lisätään rivinvaihto } // sitten loput rivit (jos on) while ( getline( f, rivi ) ) { // luetaan tiedostoa kunnes rivit loppuvat tulosta_rivi( os, rivi, onko_numero ); // tulostetaan rivi (ei otsikkona) os << endl; // lisätään vielä rivinvaihto } f.close(); // Suljetaan tiedosto. } int main( void ) { cVaihda vaihda( '|', 3 ); vaihda.tutki( "rivit.txt" ); vaihda.tulosta( cout ); return 0; }