Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: C++: Muuttujat tekstiksi stringstreamilla

Sivun loppuun

Metabolix [11.02.2006 21:08:19]

#

Tekstivirta (string stream) on käytännöllinen olio, joka toimii aivan kuin tiedostovirta, mutta tiedoston paikalla onkin tekstiä (string). Tästäkin virrasta on sekä sisään- että ulospäin toimivat versiot (istringstream, ostringstream) ja sekä sisään- että ulospäin toimiva versio (stringstream).

Käyttöesimerkkinä on funktio, jolla voi muuttaa C++:n perustyyppejä (int, float) tekstiksi ja päin vastoin, tai halutessaan muutenkin perustyyppejä toisikseen. Esitteillä on stringstream-olio. Muunnettaviksi tyypeiksi kelpaavat mitkä tahansa, jotka voi syöttää stream-olioon.

Kannattaa muistaa, että template-funktiota käytettäessä ei voi kääntää erikseen funktiota ja sitä käyttävää koodia, koska funktio käännetään tarpeen mukaan eri tyyppisillä syötearvoilla.

#include <sstream>

/* Template-funktio, joka ottaa kaksi parametriä. Template-rivillä määritellään, että TTyyppiA ja TTyyppiB ovat joitakin tyyppejä. Parametreiksi määritellään viittaus ensimmäisen tyyppiseen ja osoitin jälkimmäisen tyyppiseen muuttujaan. */
template <class TTyyppiA, class TTyyppiB>
bool Muunna(const TTyyppiA & Lahde, TTyyppiB * Kohde)
{
    std::stringstream Stream; // Stream-olio
    Stream << Lahde; // Syötetään arvo stream-olioon
    Stream >> *Kohde; // Luetaan arvo kohdemuuttujaan
    return Stream.good(); // Palautetaan tieto, onko kaikki kunnossa.
}
#include <string>
#include <iostream>

// Ja tavalla tai toisella tuo funktio tähän väliin.

int main(void)
{
    // Määritellään string, jossa on desimaaliluku
    std::string Str = "04.50";

    // Määritellään double (desimaaliluku)
    double Dbl;

    // Muunnetaan string doubleksi
    Muunna(Str, &Dbl);

    // Tulostetaan, mikä muutettiin ja minne
    std::cout << Str << " = " << Dbl << std::endl;

    return 0;
}

tn [12.02.2006 16:09:39]

#

Miksi toinen parametri (siis kohde) välitetään osoittimena? (Olettaisin syyn olevan selvyys siitä, että ko. parametria muokataan funktiossa, mutta sitä vartenhan on olemassa const-avainsana.) Itse määrittelisin funktion seuraavalla tavalla:

template <class TTyyppiA, class TTyyppiB>
bool Muunna(const TTyyppiA & Lahde, TTyyppiB & Kohde)
...

Metabolix [12.02.2006 16:16:37]

#

Juuri sen takia. Määrittele vain miten huvittaa minun puolestani. Minä tykkään laittaa sinne osoittimen.

koo [12.02.2006 21:35:19]

#

Tässä koodivinkissä on erinomaisen hyvä idea ja se on se, että tulostukseen (eli stringin rakenteluun) käytetään streameja.

Muunnosidea ei kyllä ole ihan hyvä. Kun tehdään tyyppimuunnoksia, ne kyllä hoituvat joko ihan sijoittamalla tai sitten static_castilla kiljoona kertaa tehokkaammin. Jos ei onnistu, niin silloin ollaan melko varmasti pahanteossa muutoinkin.

Kun ollaan muuntamassa jotakin stringiksi, ei oikeasti tehdä tyypinmuunnosta, vaan tulostuksen muotoilua (tai päinvastaisessa tapauksessa syötteen tulkintaa). Tulostusjutut (tai syöte-) eivät olekaan niin yksinkertaisia, vaan pitää aina varautua virhetilanteisiin ja maariippuvaisiin tapoihin. Homma voi olla myös hyvin tapauskohtaista: jos minulla on int i = 42; ja siitä pitää saada string s;, niin pitääkö olla s == "42", s == "0x2a" vai peräti s == "XLII"?

Liukuluvuilla pulma on ihan todellinen. streamien käyttö on ihan jees, mutta yleisellä tasolla Muunna-templaatti ei pelaa kovin luotettavasti.

tn:n kommentti parametreista on ihan aiheellinen. C++-koodareille on muodostunut vakiintuneita tapoja "sanoa" ja "lukea" juttuja. Kun koodissa näkyy pointteri, C++-koodari tuumailee, että ahaa, a) meillä on tässä jotakin tai se voi olla myös olematta (siis NULL), b) tässä on kyse dynaamisesta muistinhallinnasta (jota ei sellaisenaan paljon enää näekään) tai c) tämä touhu liittyy varmaankin jonkin C-rajapinnan käyttöön. Nyt ei ole kyse mistään noista, joten C++-tyylisesti funktio määritellään ihan niin kuin tn ehdottaa (paitsi että esimerkiksi minä tykkään panna const-määreen tyyppinimen jälkeen).

Mutta ihan kelpo vinkki Metabolixilta, kun siitä saa näinkin paljon tuumailtavaa :-).

Metabolix [12.02.2006 22:06:43]

#

koo: Lähinnä tässä onkin tarkoitus muuttaa juuri stringiksi ja takaisin ja näyttää, kuinka se hoituu stringstreamilla. Totta kai lukumuunnokset menevät sijoittamalla, mutta tämä vinkki juontaa juurensa taannoisesta keskustelusta, jossa koo oli näköjään itsekin osallisena.

Ajatuksenani oli tuoda ihmisten nähtäville stringstream. Minusta koodivinkkipalstan tärkein tarkoitus on juuri kertoa, mitä kaikkea on mahdollista tehdä, tai esitellä jokin poikkeuksellisen hyvä oivallus. Vinkin ei olekaan tarkoitus olla täydellisesti toimiva, virheenkäsittelyt sisältävä funktio; suoraan kopiointikelpoinen koodi on sivuseikka. Tietenkin, jos kyseessä on jokin laskennallinen asia, tilanne on aivan toinen, mutta jos käytännön asioita selitetään, niin silloin yksinkertaisettu esimerkki on minusta varsin hyvä ajatus.

Oma käyttöni tuolle funktiolle on lähinnä siinä, että saan muutettua tekstinpätkän luvuksi, kun olen ensin parsinut sen esiin tiedostosta, ja tällöin ei tule mitään äsken mainittuja ongelmia formaatin kanssa; parseri luonnollisesti tunnistaa vain tietyn muotoiset luvut, eikä yritä syöttää funktiolle muuta.

Edit: Niin ja mikäs estää käyttämästä tuota C-rajapinnassa?

int StrToInt(char * Str, int * Integer)
{
  return (Muunna(Str, Integer) ? 1 : 0);
}

Ceez [13.02.2006 16:04:15]

#

Tokihan tämä toimii myös muilla tyypeillä, kunhan vain sopivat operaattorit on määritelty.

Metabolix [13.02.2006 23:26:51]

#

Niin; kuten kirjoitin: "Muutettaviksi kelpaavat mitkä tahansa, jotka voi syöttää stream-olioon." Se on sitten aivan eri juttu, onko niissä muunnoksissa enää mitään järkeä. Ennemmin tekee suoraan muunnosoperaattorit sopiviin tyyppeihin.

koo [14.02.2006 00:29:11]

#

Niin, taisin jo sanoakin, että streamien esittely i/o-tyyppisessä asiayhteydessä on hyvä idea. Jonkun asian muotoileminen stringiksi on siis tulostamista, mikä on ihan simppeli, muttei ehkä ihan kaikille tuttu oivallus.

Arvelin, että voisi kumminkin olla ihan paikallaan kommentoida, että vaikka tässä on ihan hyvä juttu, se ei ehkä pure ihan kaikkeen ja että miksei. Pointti on siis, että (tyyppi-) muunnos on eri asia kuin tulostuksen muotoilu tai syötteen tulkinta, muutoin tuo hyvä oivallus saattaa jäädä vähän vaarallisestikin puolitiehen.

Tuota esimerkkiä voi käyttää myös C-mäisesti, sehän tarjoaa silloin ihan samat puskuriylivuoto- ja virheansat joihin C:ssä on saanut tottua. :-)

No, ei tuo StrToInt oikeastaan se ansa ole. Itse asiassa, siinä on oikein hyvä ajatus: Yleiskäyttöisen Muunna-templaattifunktion sijasta tosielämässä kannattaa tehdä rajatumpia funktioita, jolloin virhekäsittelykin on paremmin toteutettavissa.

Ceez, homma tosiaan toimii, kun operaattorit >> ja << on määritelty. Mutta jos tyypinmuunnoksia haluaa, suorempi tie on määritellä luokkiin tyypinmuunnosoperaattorit.

Hörpeli [15.02.2006 07:39:12]

#

Tämmöinen löytyi vanhojen koodien seasta.

template <class T> T to(std::string s)
{
    T t;
    std::istringstream ss(s);
    ss >> t;
    if(ss.bad())
        throw std::ios_base::failure("Terppa.");
    return t;
}

Metabolix [15.02.2006 20:57:41]

#

koo: Juu, hyvä, että joku jaksaa selittää tuollaiset asiat, kun minulle ei tullut edes mieleen, että noista tarvitsisi erikseen mainita :)

tesmu [18.04.2006 08:37:23]

#

Miten tämä tehäisiin C kielellä

Metabolix [18.04.2006 21:14:43]

#

Funktiot sprintf ja sscanf pääsevät lähimmäksi, niistä löytyy vaikkapa Googlella tietoa.


Sivun alkuun

Vastaus

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

Tietoa sivustosta