Programozási Nyelvek (C++) Gyakorlat Gyak 03. Török Márk tmark@caesar.elte.hu D-2.620 1
Kódelemzés Feladat: Olvassunk be betüket a sabványos bemenetről (a – z), és írjuk ki a nagybetűs párjukat (A – Z). Különleges karakterek, nagybetűk helyben maradnak, angol abc-vel dolgozunk.
Tömbök
Konstansokról Nézzünk egy példát: strlen implementálása: int strlen( char* s ) { char *p = s; while ( *p != '0' ) ++p; } // hello world0, előre zavarom a p-t. return p - s; // két pointer különbsége az adott szó hossza. }
Konstansokról Mi van akkor, ha: int strlen( char* s ) { char *p = s; while ( *p = '0' ) // hiba lehetőség! ++p; } return p - s;
Konstansokról Javítsuk: int strlen( const char* s ) { const char *p = s; // csak együtt lehetnek! while ( *p = '0' ) // így már szemantikai hiba! ++p; } return p - s;
Kódelemzés Áttekintés: Kezdjünk egyből C++-vel! Ha egy C++ programot írunk, érdemes a biztonságra törekedni. Azaz, kerüljük, hogy egyszerre megírjuk az egészet, és csak utána fordítunk! Részenként kell csinálni! (és úgy fordítani!) Ezek a lépések: Elso lépésként megnézzük, hogy a stdinputot másolja át a stdoutputra! Második lépésként nézzük meg, hogy felismerie a kisbetűt. Majd harmadik lépésként alakítsuk a felismert kisbetűket nagybetűssé!
Kódelemzés #include <iostream> int main() { char ch; std::cin >> std::ios_base::noskipws; while ( std::cin >> ch ) std::cout << ch; } return 0;
Kódelemzés Megoldás: #include <iostream> using namespace std; int main() { char ch; while (cin >> noskipws >> ch) { cout << …(???) } } noskipws: io-manipulátor, nem ugorja át a whitespaceket (space, tab,…), ennek testvére a skipws, mely átugorja azokat.
Kódelemzés Fordul! Yeehaaa! Kisbetűk felismerése a feladat!
Kódelemzés #include <iostream> int main() { char ch; std::cin >> std::ios_base::noskipws; while ( std::cin >> ch ) if ( 'a' <= ch && ch <= 'z') // #1 std::cout << ch + 'A' - 'a'; // #2 } else std::cout << ch; return 0;
Kódelemzés #1 Kérdés: Működik-e char-ok között a <= operator? Mivel mindegyik int-re konvertálódik, így az ascii kódok között történik meg a <= vizsgálat! #2 Kérdés: Hogyan konvertáljuk a karaktereket nagybetűvé? Mivel ascii-val dolgozunk, ezért ch + 'A' - 'a'
Kódelemzés int-ek kerülnek kiírásra, mivel a + és - szintén nincs értelmezve a char-ok között! ascii kód íródik ki, ahelyett, hogy char érték íródott volna ki!
Kódelemzés #include <iostream> int main() { char ch; std::cin >> std::ios_base::noskipws; while ( std::cin >> ch ) std::count << 'a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch; } return 0;
Kódelemzés Mi történt? A kiértékelés miatt precedenciaproblémák vannak!
Kódelemzés #include <iostream> int main() { char ch; std::cin >> std::ios_base::noskipws; while ( std::cin >> ch ) std::count << ('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch); } return 0;
Kódelemzés Továbbra is számok íródnak ki! Meglepő módon most már a betűk helyett is számok íródnak ki! T1 T2 ==> T Fordítási időben meg kell mondania, hogy melyik kiíró-operátort válassza meg! A fordítónak fordítás alatt tudnia kell, hogy milyen a kifejezés típusa! Itt: int op char => int
Kódelemzés Promotion rules: short, char => int float => double double => long double Odafelé jól mentek a dolgok, maguktól mentek a konverziók! Visszafelé már nem!
Kódelemzés Megoldások: char(i), ha i : integer, akkor i-t char-ra konvertáljuk. static_cast<char>(i) (Később) char ch = i;
Kódelemzés #include <iostream> int main() { char ch; std::cin >> std::ios_base::noskipws; while ( std::cin >> ch ) std::count << char('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch); } return 0;
Kódelemzés Más lehetőség: Saját toupper metódus írása! Amit egyszer már megírtak, azt ne írjuk meg mégegyszer! Beépített toupper metódus használata
Kódelemzés Írjunk olyan utility-t, ami úgy működik, mint egy unixparancs. Ha nem adunk paramétert, akkor stdinput/outputot használja, ha adunk paramétert, akkor azt, mint fájlt akarja használni!
Kódelemzés #include <iostream> int main(int argc; char *argv[]) { ... }
Kódelemzés #include <iostream> void toupper(std::istream&, std::ostream&); int main( int argc; char *argv[] ) { if ( argc < 2 ) toupper(std::cin, std::cout); }
Kódelemzés Akár van fájl, akár nincs, ugyanazt csinálom, ezzel megóvom magamat a dupla munkától! az istream, ostream osztályoknak a copyconstruktora private, hogy ne lehessen másolni, így mindig referencia szerint adom át öket paraméternek.
Kódelemzés #include <fstream> #include <iostream> void toupper(std::istream&, std::ostream&); int main( int argc; char *argv[]) { if ( argc < 2 ) toupper(std::cin, std::cout); } else // folyt. return 0;
Kódelemzés for ( int i=1; i < argc; ++i ) { // Meg kell nyitni a fájlt! ifstream inp(argv[i]); if ( !inp ) std::cerr << "Can't open" << argv[i] << std::endl; } else toupper(inp, std::cout);
Kódelemzés Kérdés: Kell-e close-t mondanom? Amikor a zárójelet becsukom, akkor az ifstream destruktora meghívódik!
Kódelemzés Feladat: Számoljuk meg, hogy a bemeneten hány sor volt. (Sorvége-jel: ‘\n’)
Kódelemzés #include <fstream> #include <iostream> void lines(std::istream&, std::ostream&); int main( int argc; char *argv[]) { if ( argc < 2 ) lines(std::cin, std::cout); } else // folyt. return 0;
Kódelemzés for ( int i=1; i < argc; ++i ) { ifstream inp(argv[i]); if ( !inp ) std::cerr << "Can't open" << argv[i] << std::endl; } else lines(inp, std::cout);
Kódelemzés Egy adott karakter előfordulása egy egyszerű számlálás!
Kódelemzés void lines( std::istream& inp, std::ostream& outp ) { int cnt = 0; char prev = '\n'; char curr; while ( std::cin.get(curr) ) cnt = f(cnt, prev, curr); prev = curr; }
Kódelemzés int f( int cnt, char prev, char curr ) // warning! { if ( '\n' == prev ) ++cnt; } return cnt;
Kódelemzés int f( int cnt, char prev, char ) // így már nem! { if ( '\n' == prev ) ++cnt; } return cnt;
Kódelemzés Javítás: void lines( std::istream& inp, std::ostream& outp ) { int cnt = 0; char prev = '\n'; char curr; while ( std::cin.get(curr) ) cnt += '\n' == prev; prev = curr; }
Kódelemzés Megoldás: #include <iostream> int main() { int ls = 0; char c; while ( std::cin >> std::noskipws >> c) { if (c == ‘\n’) { ls += 1; } } std::cout << ls << std::endl; return 0; }
Kódelemzés Megoldás: más út #include <iostream> int main() { int ls = 0; char c; while (std::cin >> std::noskipws >> c) { ls = (c == ‘\n’ ? ls + 1 : ls); } std::cout << ls << std::endl; return 0; }
Kódelemzés Kimenet: alma szilva ctrl-D eredmény: 2 alma szilva ctrl-D eredmény: 1
Kódelemzés Feladat: Írjuk át úgy a programot, hogy ne az ‘\n’ karaktereket keressük, mert az utóbbi esetben hibás a végrehajtás.
Kódelemzés Megoldás: int f ( char prev, int ls) { return prev == ‘\n’ ? ls + 1 : ls; } char c, prev; while ( std::cin >> std::noskipws >> c) { ls = f(prev, ls); prev = c; }
Kódelemzés Feladat: Szavak számának a számolása. alma (1) „ „ szilva (2) …
Kódelemzés Megoldás: int f (char prev, char c, int ls) { return ( prev == ‘\n’ || prev == ‘\t’ || prev == ‘ ‘) && c != ‘\n’ && c != ‘\t’ && c != ‘ ’ ? ls + 1 : ls; }
Kódelemzés Megoldás: Más út bool isWS(char c) { … }