Olisi tälläinen lauseke:
cout << "Anna talon sivun pituus(m): ";
cin >> Talo.x;
Tarvitsisin jonkin koodipätkän joka tarkistaa onko annettu Talo.x:n arvo oikea eli se sallii ainoastaan numeroarvot. Kokeilin keskustelupalstalta löytynyttä ratkaisuja mutta en saanut yhtään näistä toimimaan muutaman tunnin väsäyksenkään jälkeen.
Onko teillä antaa opastusta?
#include <iostream> using namespace std; int main(int argc, char ** args) { int x; cout << "Anna talon sivun pituus(m): "; cin >> x; if (!cin.good()) cerr << "Virheellinen syöte"; else cout << "Talon pinta-ala on " << x*x; return EXIT_SUCCESS; }
Tuota koodia katsomalla ei pääse vielä hakemaani tulokseen (uskoisin näin) elikkä tarkemmin:
-Koodi tulee keskelle ohjelmaa
-tarkistaa onko annettu merkkijono varmasti numero
-Jos kyseessä on muita merkkejä kuin numeroita niin kysyy sivun pituuden uudestaan.(oletetaan että vastaus tulee pelkästää numeroina)
Kokeilin https://www.ohjelmointiputka.net/keskustelu/8100-annetun-arvon-todentaminen
löytyviä malleja mutta ainostaan sain lisää virheitä aikaan ohjelmaan.
#include <iostream> using namespace std; int kysy_luku(char * kysymys, char * virhe) { int x; while (1) { cout << kysymys; cin >> x; if (!cin.good()) cout << virhe; else return x; // Palautetaan x } } int main() { int x; x = kysy_luku("Anna talon sivun pituus: ", "Ei kelpaa.\n"); cout << "Talon pinta-ala on " << x*x; return EXIT_SUCCESS; }
En kokeillut, mutta eiköhän tuo toimi. Pitäisi se nyt itsekin osata laittaa silmukkaan tuosta FooBatin esimerkistä.
Otsikkotiedoston ctype.h funktio isdigit()
Uusi ongelma, uudet kuviot...
Elikkäs
cout << "Anna 1. murtoluvun osoittaja: "; cin >> syote; if (!isdigit(syote)) cout << "\nAntamasi syöte ei ollut numero!"; luku1.osoittaja = 1; else luku1.osoittaja = syote;
Tuollainen koodipätkä. Tarkoituksena olisi että jos käyttäjä antaa virheellistä tieto, ohjelma herjaa siitä ja kysyy uudestaan asiaa. Mitenkä ko. asia onnistuisi kätevästi? Nyt se vain sijoittaa osoittajaksi 1:sen. Tähän aina voisi käyttää kaikkien rakastamaa GoTo -hommaa mutta sitä on joka paikassa kehotettu välttämään. Mikä olisi vaihtoehtoinen lähestymistapa?
Puuttuu muuten aaltosulut. Eihän tuo voi toimia.
Minun tapani on se goto. Tällaiseen se sopii aivan hyvin. Sitä käsketään välttää, koska jos joku aloittelija keksii, että tämäpä on kiva, niin sitten sitä löytyykin joka välistä aivan turhaan. Esimerkiksi uuden funktion korvikkeena heitetäänkin vain epämääräisiä gotoja sinne tänne, kun se mukamas on helppoa ja selkeää. Laiskuutta se on, ja tyhmyyttä, kun ei jaksa opetella käyttämään funktioita eikä voi myöntää, ettei osaa :)
Oikein käytettynä goto ei ole paha asia, ja joskus se jopa helpottaa asioita suunnattomasti ja säästää ylimääräiseltä muuttujalta ja parilta ehtolauseelta. Voin kertoa esimerkiksi, että eräästä minun parin sadan kilon (muutaman tuhannen rivin) lähdekoodistani löytyy peräti pari kolme gotoa. Hui kauhistus. Kyllä sitä ihan ammattilaisetkin käyttävät, ei se pure.
Kysy: cout << "Anna 1. murtoluvun osoittaja: "; cin >> syote; if (!isdigit(syote)) { cout << "\nAntamasi syöte ei ollut numero!"; goto Kysy; } // Tänne päästään, kun tulee kunnollinen syöte luku1.osoittaja = syote;
Muiden tapa, mukamas parempi...Silmukat.
while (1) { cout << "Anna 1. murtoluvun osoittaja: "; cin >> syote; if (!isdigit(syote)) cout << "\nAntamasi syöte ei ollut numero!"; else { luku1.osoittaja = syote; break; // break; vie ulos while- tai for-silmukoista. } }
Ok, kiitoksia. Sain tehtyä Metabolixin esimerkin mukaan tälläisen:
luku1.osoittaja = kysy_luku("Anna 1. murtoluvun osoittaja: ", "Ei kelpaa.\n"); //luku1.osoittaja arvoa double int kysy_luku(char * kysymys, char * virhe) { char x; //<- tässä mättää, jos annna x:n int:ksi niin homma kosahtaa siihen että tulokseksi ei voi antaa muita kuin kirjaimia ettei ohjelma kaadu while (1) { cout << kysymys; cin >> x; if (!isdigit(x)) cout << virhe; else return x; } }
Ongelmana on että x palautuu char merkkisenä varmaankin sillä kun käytän saatua tulosta laskuissa se käyttää niissä sitä numeroa vastaavaa ASCII-arvoa. Missä vika? Toinen ongelma on että x:n ei huoli kuin yhden merkin. Voiko tämän kiertää jotenkin kätevästi vai pitääkö annettu syöte pilkkoa palasiksi ja verrata silmukassa niitä?
Jos tekisit, kuten aiemmin neuvottiin, eli käyttääisit cin.good(), homma toimisi. Käytä siis aiemman esimerkkini mukaista kysymisfunktiota tai muokkaa se sopivaksi.
pienet kommentit koodissa ei olisi pahitteeksi. Eli mitä tuo cin.good() tekee? Oletettavasti tarkistaa syötteen käykö se annettuun tyyppiin?
Pienellä kokeilulla jos vaihdan x:n tyypiksi Integerin niin homma pelittää muuten mutta jos siihen annetaan esim. kirjain, jää ohjelma ikuiseen silmukkaan tulostamaan "Anna 1. murtoluvun osoittaja: Ei kelpaa." Jos x:n tyyppi on char se ei jää silmukkaan mutta laskee myöhemmin ohjelmassa laskut ko. merkin ASCII-arvolla. Miten tämän kohdan voisi kiertää?
Tuosta tosiaan puuttuikin puskurin tyhjennys, kun se ei syystä tai toisesta C++:lla oikein helposti onnistu. Helpommalla pääsee, kun käyttää C:n funktioita, jotka siis toimivat myös C++:ssa:
#include <stdio.h> int kysy_luku(char * kysymys, char * virhe) { int x; while (1) { printf("%s", kysymys); // Luetaan syöte ja palautetaan, jos se on ok, eli jos scanf on saanut luetuksi yhden arvon if (scanf("%i", &x) == 1) return x; // Tyhjennetään kaikki rivinvaihtoon asti while (fgetc(stdin) != '\n'); // Tulostetaan virhe. printf("%s", virhe); } }
char-muuttujatyyppi on juuri se merkki. Jos hlauat useampinumeroisia lukuja lukea, tee char-taulukko, lue siihen, tarkista jokainen merkki erikseen ja lopuksi siirrä kokonaisluvuksi sscanf:llä.
#include <iostream> #include <stdio.h> char char_taulu[11]; // Funktioon... Alku: std::cout << "Anna luku: "; std::cin.getline(char_taulu, 11); for (int i = 0; char_taulu[i] != 0; i++) if (!isdigit(char_taulu[i])) { std::cout << "Virhe!\n"; goto Alku; } sscanf(char_taulu, "%i", &luku);
Onpa muuten kömpelö tapa, ainakin minusta.
//***Funktiot int kysy_luku(char *kysymys,char *virhe) cout << kysymys; cin.getline(char_taulu, 11); //Tarkistetaan onko negatiivinen arvo if (char_taulu[0] == 45) { a = true; i=2, j=1 while (i<12) { chartaulu[j] = char_taulu[i]; i++; j++; } } //***Tarkistetaan onko oikeanlainen syöte for (i = 0; char_taulu[i] != 0; i++) if (!isdigit(char_taulu[i])) { cout << virhe; kysy_luku; //<- Tarkoitus olisi että siirtyisi funktion alkuun } if (a = false) { return(atoi(char_taulu)); else return(atoi(char_taulu) * (-1)); }
Ok, tälläinen koodi jota kutsutaan tälläisellä rivillä...
luku1.osoittaja = kysy_luku("\nAnna 1. murtoluvun osoittaja: ", "\nVirheellinen syöttö!");
Mistä virheet johtuu? Olen yrittänyt väsätä ohjelmaani toimimaan tälläisellä funktiolla jo jonkin aikaa mutta millään en ole saanu toimimaan. Ko. ohjelma toimii jos jokainen kohta kysytään erikseen mutta se tekee sorsasta jo aika tönkön ja epäselvän. Tässä olisi pari kohtaa missä erityisesti tarvitsen apua.
1)Negatiivisuuden tarkistus Char_taulusta. Onko se juuri noin '-'-merkin tarkistus sen ASCII arvolla char_taulun ekasta lokerosta? Voiko kyseisen arvon muuttaa NULL:ksi vai pitääkö koko taulu muokata kuten yllä teen?
2)Miten ko. funktion saisi toimimaan noin ylipäätään. Tällä hetkellä se suoltaa noin 48 virhettä mitä en jaksa postailla tänne.
Yleensä virheissä lukee, mistä ne johtuvat. Opettele siis lukemaan niitä. Korjaa nyt ensin syntaksi kohdalleen. Saat tehdä sen ihan itse, että opit. Vihjeenä, että aaltosulkuja puuttuu / on väärissä paikoissa, ja vertailuoperaattori on ==
eikä =
ja että on selkeämpää käyttää miinusmerkin tarkistuksessa miinusmerkkiä ('-'
) kuin epämääräistä ascii-arvoa. i:n ja j:n arvojen asettelut ovat väärin, ja lienetkö edes määritellyt koko muuttujia? Funktion alkuun pääsee sopivalla gotolla tai sopivalla silmukalla, edellisissä viesteissäni on molemmista esimerkkejä. Sanotaan vielä lopuksi, että atoi ei ole standardifunktio, käytä mieluummin tuota aiemmin mainitsemaani sscanf:ää.
Edellisessä viestissäni on muuten täysin toimiva funktio tuohon, jos laiskuus iskee. Kannattaa kuitenkin opetella itsekin tekemään jotakin, niin säästyy vaivaamasta muita syntaksivirheillään! Kääntäjä kyllä kertoo, mikä on vikana.
Jep, sain homman pelittämään vihdoin ja viimein oikein. Anteeksi näistä syntaksivirheiden tänne suoltamisesta mutta niitä ei oikein huomaa enää 3 tunnin koodauksen jälkeen kun koko ajan yrittänyt saada tuota toimimaan oikein :) Kiitoksia paljon avusta Metabolix. Kyllä minä varmaan joskus osaan koodata oikeinkin. Pitänee tutustua tarkemmin miten nuo printf ja scantf toimii vielä...
printf:stä ja scanf:stä löytyy jotakin mm. Putkan C-opassarjasta ja lisää sitten vaikkapa täältä (selaa "See Also" -linkkejä pitkin, niin kohta osaatkin jo kaiken :). Syntaksivirheitä taas ei oikein voi olla huomaamatta, kun kääntäjä sanoo aivan selkeästi, mistä on kysymys. Virheilmoituksesta löytyy lähes aina rivinumero ja poikkeuksetta ainakin itse virhe.
Metabolix kirjoitti:
Sanotaan vielä lopuksi, että atoi ei ole standardifunktio, käytä mieluummin tuota aiemmin mainitsemaani sscanf:ää.
atoi
on (ainakin) ANSI-, POSIX- sekä ISO-9899-C:tä. Ihan tarpeeksi standardia siis.
Ajattelit ehkä itoa
-funktiota, joka ei ole standardi.
Deewiant kirjoitti:
Ajattelit ehkä itoa-funktiota, joka ei ole standardi.
Ai joo, niinpä tosiaan. No mutta sscanf on silti parempi kuin atoi, koska siltä saa tarvittaessa virheilmoituksen ja lisäksi se on paljon monipuolisempi funktio.
Tarvitseeko noita cout ja cin juttuja sotkea tonne, jos C:llä on muuten. Itse olen ainakin nämä ymmärtänyt prtinf ja scanf:in C++ vastaaviksi.
Voit käyttää vallan mainiosti printf- ja scanf-funktioita cout- ja cin-olioiden asemesta.
Tarvitseeko noita printf ja scanf juttuja sotkea tonne, jos C++:lla on muuten?
No ei näköjään. Bufferin saa kuin saakin tyhjennettyä rivinvaihtoon asti C++:llakin:
cin.clear(); while ('\n' != (char)cin.get()) {}
Kokeilin tätä jo aiemmin, mutta se ei silloin toiminut. Nyt se vain yhtäkkiä alkoi toimia. Kummallista.
Tuo aiemmin C:llä kirjoitettu luvunkysymisfunktio näyttäisi siis C++:lla tältä:
#include <iostream> using std::cin; using std::cout; int kysy_luku(char * kysymys, char * virhe) { int x; while (1) { cout << kysymys; // Luetaan syöte ja palautetaan, jos se on ok cin >> x; if (cin.good()) return x; // Virhemerkintä pois ja tyhjennetään kaikki rivinvaihtoon asti cin.clear(); while ('\n' != (char)cin.get()); // Tulostetaan virhe. cout << virhe; } }
Uusi pulma. Nyt tiedot syötetään char_taulu[11] nimiseen muuttujaan. Voiko käyttäjää helposti estää laittamasta ko muuttujaan enempää kuin ne max 10 merkkiä? Käytän todellakin noita c++ Cin/Cout-olioita. Tätä nykyä ohjelma kaatuu aina kun siihen syötetään niin paljon merkkejä että char_taulu[11] täyttyy.
Jos käytät c++:aa, olisi ehkä suositeltavampaa ottaa se string-luokka käyttöön...
Joo, taidan jättää tähän ohjelmaan vielä tuon char_taulun ja kokeilen ottaa sitten tuon string-luokan käyttöön seuraavaan harkkaan. Lähinnä siitä syystä että en osaa käyttää string-luokkaa vielä ja ohjelma pitää palautella huomenna. Se toimii nyt jo melko hyvin niin en jaksa käydä sorkkimaan enää.
Seuraavaa harkkaa ajatellen miten tuo minun funktioni olisi kääntynyt string:iä käyttäen?
Ensimmäiseen tepsii muistaakseni width:
cin.width(10); cin >> char_taulu;
Ehkä seuraavasta on apua string-luokan saloihin perehtymisessä (en kääntänyt, joten voi sisältää virheitä). Ei muuta kuin jokin parempi oppikirja käteen ja tutkimaan:
#include <string> #include <iostream> int main() { std::string s; std::getline( std::cin, s ); std::cout << "s:n sisältö oli: " << s << std::endl; return 0; }
Uusi pulma, miten saan tuossa ylläolevassa kysy_luku funktiossani tarkisteltua että syöte on oikein?
Nyt se kelpuuttaa vain numerot mutta haluan että se huolii myös etumerkit ''+ ja '-'-merkit ja liukuluvut elikkäs '.'-merkin. Tämä varmaan onnistuu ASCII-arvoja tarkistelemalla (etumerkit 43,45,46 ja numerot 48-57). Miten pystyn tarkastelemaan noita ASCII-arvoja syötteestä? Kokeilin lisätä tuohon if-lauseeseen vain ASCII-arvot mutta se tulkitsee ne vain numeroiksi.
double kysy_luku(char *kysymys,char *virhe) { string s; //<- Kokeillaan c++ läheisempää tietotyyppiä bool error = false; //Toistetaan silmukkaa kunnes virhe väistyy do //<-Saavutaan tähän virheen sattuessa kyselyssä { cout << kysymys; cin >> s; // Tehdään niin kauan, kunnes i on suurempi kuin // merkkijonon pituus. Jos kohdataan muu kuin 0-9 // merkki, lopetetaan virheilmoituksen kera. for(int i=0;i<s.size()-1;i++) { if(s[i]>='0'&&s[i]<='9'||s[i]=='.'||s[i]=='+'||s[i]=='-') { cout << virhe; break; } } } //Jos sattui virheellinen syöte siirrytään Do-silmukan alkuun while (error == true); //Palautetaan luku return(atof(s)); //<- VALITTAA TÄSTÄ! }
Tälläisen funktion sain aikaan mutta vaikka virheentarkistuksen pitäsisi nyt toimia en voi kääntää ohjelmaa sillä se valittaa atof-funktiosta "cannot convert parameter...". Miten ongelman voi välttää käyttämällä string-tyyppiä?
Kun luet sen virheen ajatuksella, niin varmaan ymmärrät itsekin. Sehän sanoo aivan selvästi, ettei string-tyyppi kelpaa char*-tyypiksi. Ratkaisuna string::c_str()-funktio.
return(atof(s.c_str()));
Hi, Smee again. Goan and fix my code...
Elikkäs uusi päivä, uudet ongelmat.
Minulla on aikaisemmasta char_taulua käyttävästä esimerkistäni johdettu funktio. Tämä valittaa että ei voi konvertoida "char to char[512]". Funktion tyyliksi on vaihdettu char mutta voiko jostain laittaa että funktio palauttaa char[512] tyyppisen merkkijonon?
Ei voi. Se ei C:ssä toimi niin. Taulukko on nimittäin osoitin.
Voit kyllä tehdä niin, että annat tuollaisen funktiolle syötteenä, ja funktio käsittelee sitä. Vaikka näin:
void Laita_Tekstiksi_Kissa(char *Teksti) { Teksti[0] = 'K'; Teksti[1] = 'i'; Teksti[2] = 's'; Teksti[3] = 's'; Teksti[4] = 'a'; Teksti[5] = 0; } // Ja main-funktio: int main(void) { char Tekstitaulu[10]; Laita_Tekstiksi_Kissa(Tekstitaulu); cout << Tekstitaulu << endl; // Tulostaa tekstin "Kissa" return 0; }
Periaatteessa funktiossa voidaan varata muistia char taulukolle ja palauttaa uuden taulukon osoitin. Tämä ei kuitenkaan ole kovin suositeltava tapa, koska varatut merkkijonot pitäisi jossain välissä muistaa vapauttaa.
Kiitokset avusta, tulikin mieleen että ei tarvitse palauttaa arvoa kun käyttää opettajan kaihtamia globaaleja muuttujia. Homma pelittää taas toistaiseksi hyvin.
Kyllä niitä globaaleja muuttujia voi silti kaihtaa, ettei satu vahinkoja. Suosittelen parametritapaa.
Ongelma:
Pitää järjestää tietuetaulukossa olevat tiedot aakosjärjestykseen. Miten tämä hoituisi kätevästi ja yksinkertaisesti? Kokeilin itse seuraavaa ratkaisua mutta valittaa että "strncmp ei voi konvertoida 'char' to 'const char *' Tässä ote koodista
void jarjesta(void) { char temp1[512], temp2[512], temp3[512] = ""; int i,j,y = 0; for (i=0;i<50;i++) { if (strncmp(tietokanta[i].merkki[j], tietokanta[i+1].merkki[j], 3) > 0) { for (y=0;y<512;y++) { temp1[y] = tietokanta[i].merkki[y]; temp2[y] = tietokanta[i].malli[y]; temp3[y] = tietokanta[i].vuosimalli[y];
tietokanta on globaali. Tarkoituksena että strncmp-funktiolla vertaa ja sen mukaan vaihtaa tietojen paikkaa. Miten tuon ylläolevan voisi kiertää? Btw, ei oo hajuu mikä on parametritapa.
Mikä on tuon j:n tarkoitus, jos se on kuitenkin nolla? No joka tapauksessa, strncmp haluaa char-osoittimen, eli &-merkki parametrin eteen tai [j] pois.
Kiitoksia jälleen. Näyttäisi ohjelman puolesta olevan ok koodia vaikka en toistaiseksi tajua miksi tuo viittaus pitää olla. Pitänee selvittää perusteellisesti ko. seikka jatkoa ajatellen. Muuten ohjelma tosin kaatuu tuohon funktioon. Pitää tarkastella sitä hieman tarkemmin ja ajatuksen kanssa läpi.
Varmaankin juuri siksi, että tuossa vertailussa on se j-indeksi merkistä, vaikka j:tä ei ole alustettu mihinkään järjelliseen arvoon. int i,j,y = 0;
alustaa vain y:n, vaikka j on ainut, joka kaipaa erillistä alustusta.
Aihe on jo aika vanha, joten et voi enää vastata siihen.