Ebben a tananyagrészben arról lesz szó, hogy hogyan tudunk utasításokat ismételten végrehajtani abban az esetben, ha az ismétlések száma nem ismert a program számára. Továbbá szó lesz még az alapvető fájlkezelésről.
Előző tananyagrész: tömb, std::array, std::vector, for ciklus
Következő tananyagrész: do-while ciklus, input ellenőrzés
while ciklus
A while ciklus azoknak az eseteknek a kezelésére lett kitalálva, amikor nem ismert a program számára, hogy hányszor kell megismételni bizonyos utasítások végrehajtását.
A while ciklus tulajdonképpen olyan mint egy if (else ág nélkül), azzal a különbséggel, hogy nem csak egyszer fut le, hanem egészen addig, amíg az általunk megadott feltétel érvényben van (tehát true az értéke). Amikor a feltétel már nem teljesül, a ciklusmag nem fut le (a ciklus leáll).
A while ciklus szintaxisát szemléltető kód (ez csak szemléltető kód, nem használható, fordítási hibát okoz):
while ( /*condition*/ ) {
//statements executed while the condition is true
}
Az elöltesztelős ciklusok (for, foreach és while) struktogramja:
Sokan használnak while ciklust for ciklus helyett, ugyanúgy számlálós ciklusként. Szerintem ennek kerülendőnek kéne lennie.
//use for loop instead of this
int i = 0;
while (i < n) {
//statements
++i;
}
Amikor egy ciklusváltozót növelgetünk egy, a programunk számára ismert számig, akkor arra inkább for ciklust használjunk.
1. while ciklus példa: egy egész szám számjegyeinek száma
Egy int típusú szám tízzel való (egész)osztása valójában az adott szám legkisebb helyiértékű számjegyének elhagyását jelenti. Pl. ha int típusú 128-at osztunk szintén int típusú tízzel, annak eredménye 12 lesz.
Ha egy int típusú szám minden egyes tízzel osztását követően növelünk egy számlálót, akkor megkapjuk az adott szám számjegyeinek számát.
A számlálónak adjunk kezdőértéket, különben az eredmény nem lesz megfelelő.
//integer digits count example
#include <iostream>
int main () {
long int input_number;
std::cout << "Please enter an integer number:\n";
std::cin >> input_number;
std::cout << "The input number: " << input_number << '\n';
int digits_count = 1;
while ( input_number /= 10 ) {
digits_count++;
}
std::cout << "The number of digits: " << digits_count << '\n';
}
Kapcsolódó tananyagrész:
Persze akár azt is meg lehetne tenni, hogy egy std::string típusú változóba kérjük be a felhasználótól a számot, és a size() tagfüggvénnyel lekérdezzük, hogy hány karaktert tartalmaz, bár ez nem tartalmaz while ciklust.
//integer digits count example
#include <iostream>
#include <string>
int main () {
std::string input_number;
std::cout << "Please enter an integer number:\n";
std::cin >> input_number;
std::cout << "The input number: " << input_number << '\n';
std::cout << "The number of digits: " << input_number.size() << '\n';
}
2. while ciklus példa: split
A split műveletben egy stringet valamilyen elválasztókarakter (általában szóköz) mentén feldarabolunk, és az egyes szavakat egy tömb egyes elemeinek adjuk értékül.
Más programozási nyelvekben a tömbök új elemekkel bővíthetőek, a C++ nyelvben erre az std::vectort használhatjuk.
//split example
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
int main() {
//init
std::string string_example = "The quick brown fox jumps over the lazy dog";
char delimiter = ' ';
std::vector<std::string> words;
std::stringstream sstream_example(string_example);
//split
std::string word;
while (getline(sstream_example, word, delimiter)) {
words.push_back(word);
}
//output
for (const std::string& element : words) {
std::cout << element << '\n';
}
}
3. while ciklus példa: soronkénti beolvasás fájlból
Elsőként hozzunk létre egy fájlt ugyanabban a könyvtárban, amiben a programunkat fogjuk elhelyezni. A fájl tartalma legyen mondjuk számértékek felsorolása:
1 0 3 6 8 3 4 6
4 6 0 4 7 9 1 5
5 9 1 3 2 0 3 4
Egy ilyen utasítást követően a file_example nevű fájl objektumon keresztül elérhetjük a programunkban azt a fájlt, ami a programunk futtatható fájljának könyvtárában található filename.txt néven:
ifstream file_example("filename.txt");
Az ifstream típusú file_example nevű fájl objektum követi nyomon, hogy hol tart a fájl feldolgozása.
Ezt követően ha létrehozunk egy std::string változót (pl. std::string row; utasítással), akkor a getline(file_example,row); utasítással beolvashatjuk a row nevű, std::string típusú fájlba a fájl egy sorát.
Két ellenőrzést azonban el kell végeznünk.
Egyrészt, hogy sikerült-e megnyitni a fájlt (pl. hibalehetőség ha nem létezik a fájl, vagy nem a megfelelő könyvtárban van), amit a fájl objektum is_open() tagfüggvényével tehetünk meg.
Másrészt pedig, hogy a fájl végére értünk-e, amit a fájl objektum eof() tagfüggvényével kérdezhetünk le. Az eof() tagfüggvény akkor ad vissza igazat, ha a fájl végére értünk, ezért a ciklusnak addig kell futnia, amíg az eof() tagfüggvény hamis értéket ad visszal, amit így fejezhetünk ki: !file_example.eof()
Ebben a példában kiíratjuk a standard outputra (alapesetben a parancssorba) a fájl sorait. Miután ezzel végeztünk, a fájl feldolgozását lezárjuk a fájl objektum close() tagfüggvényével.
if ( file_example.is_open() ) {
while ( !file_example.eof() ) {
getline(file_example, row);
std::cout << sor << '\n';
}
file_example.close();
} else {
std::cout << "Error opening file" << '\n';
}
A teljes példaprogram:
//print the rows of a file to stdout
#include <iostream>
#include <string>
#include <fstream>
int main() {
std::fstream file_example( "example_1.txt" );
std::string row;
if ( file_example.is_open() ) {
while ( !file_example.eof() ) {
getline(file_example, row);
std::cout << row << '\n';
}
file_example.close();
} else {
std::cout << "Error opening file" << '\n';
}
}
Előfordulhat, hogy könyvekben, internetes példákban ennek a kódrészletnek egy kicsit módosított változatával találkozunk:
while ( getline(file_example, row) ) {
std::cout << row << '\n';
}
4. while ciklus példa: szavankénti beolvasás fájlból
Hozzunk létre egy fájlt abban a könyvtárban, amiben majd a programunk indítófájlja lesz, például ezzel a tartalommal:
Budapest Prague Vienna Bucharest
New York Washington Toronto
Moscow Sanghai Pongyang
Az előző feladathoz hasonlóan szintén létre kell hoznunk egy fájl obkejtumot, aminek a paramétereként meg kell adnunk, hogy milyen nevű fájllal kívánunk dolgozni:
fstream file_example("filename.txt");
Ezt követően létre kell hoznunk egy (jellemzően std::string típusú) változót amibe egyenként beolvassuk a fájl egyes szavait, ezt nevezzük mondjuk word-nek.
Ha szavanként végezzük el a fájlból beolvasást, akkor a getline függvény helyett az std::cin-hez hasonlóan végezzük el a bekérést:
file_example >> word;
Ezelőtt persze az előző feladathoz hasonlóan el kell végeznünk a két ellenőrzést, hogy a fájlt sikerült-e megnyitni, illetve hogy nem ért-e a fájl végére a feldolgozás.
A while ciklust követően bezárjuk a fájlt.
//print the word of a file to stdout
#include <iostream>
#include <string>
#include <fstream>
int main() {
std::fstream file_example("example_2.txt");
std::string word;
if ( file_example.is_open() ) {
while ( !file_example.eof() ) {
file_example >> word;
std::cout << word << ' ';
}
file_example.close();
} else {
std::cout << "Error opening file" << '\n';
}
}
Hozzáfűzés fájlhoz
Bár ez nem while ciklusos példa, hiszen ismerjük azon dolgok számát, amiket egy fájlba ki akarunk írni, a fájlkezeléshez viszont fontos ismeret lehet.
Ebben a példában egy std::vectorban tárolt értékeket írunk ki egy fájlba.
Szintén szükségünk lesz egy fájl objektumra. Ha a fájlhoz hozzáfűzni szeretnénk, fontos, hogy a második paraméter std::ios::app legyen (alapesetben nem ez van kiválasztva).
std::fstream file_example( "filename.txt", std::ofstream::app );
A for cikluson belül szinte ugyanúgy írjuk ki az elemeket a fájlba, mintha csak a standard outputra írnánk. Ebben a példában össze is lehet hasonlítani a kettőt, mivel a standard outputra is kiírjuk, amit a fájlba kiírtunk.
//print the rows of a file to stdout
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
int main() {
std::vector<std::string> strings_example = {"Budapest", "Prague", "Warsaw", "Vienna", "Bratislava"};
std::fstream file_example( "filename.txt", std::ofstream::out | std::ofstream::app );
if ( file_example.is_open() ) {
for (const std::string& element : strings_example) {
file_example << element << ' ';
std::cout << element << ' ';
}
file_example << '\n';
std::cout << '\n';
file_example.close();
} else {
std::cout << "Error opening file" << '\n';
}
}
Fájl tartalmának felülírása
Az előző példához hasonló, csak a fájl objektum második paramétere std::ofstream::trunc lesz, nem pedig std::ofstream::app.
//print the rows of a file to stdout
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
int main() {
std::vector<std::string> strings_example = {"Budapest", "Prague", "Warsaw", "Vienna", "Bratislava"};
std::fstream file_example( "filename.txt", std::ofstream::trunc | std::ofstream::out);
if ( file_example.is_open() ) {
for (const std::string& element : strings_example) {
file_example << element << ' ';
std::cout << element << ' ';
}
file_example << '\n';
std::cout << '\n';
file_example.close();
} else {
std::cout << "Error opening file" << '\n';
}
}
Egyéb tananyagok:
Előző tananyagrész: tömb, std::array, std::vector, for ciklus
Következő tananyagrész: do-while ciklus, input ellenőrzés