(toc)
Reguláris kifejezések
A reguláris vagy szabályos kifejezések bizonyos szabályokat követő
karaktersorozatok. Arra szolgálnak, hogy megkönnyítsék a szöveggel való munkát,
mint a keresés, helyettesítés, ellenőrzés, stb. Az ilyen kifejezések sok programozási
nyelvbe be vannak építve, főleg a szövegek feldolgozására specializálódott
nyelvek, mint az XML vagy a HTML. A C++11 is szabványosította ezeket a
kifejezéseket a <regex> könyvtár révén. A reguláris kifejezések szabályai
eltérőek lehetnek, ezért több szabályzat
(szintaxis) sorolható fel. Ezek közül a C++11 az ECMAScript, az alap és
bővített POSIX, az awk, a grep és az egrep szabálygyűjteményeit támogatja. A
következő példákban az ECMAScript szintaxisát fogjuk használni.
A reguláris kifejezéseket fokozatosan kell felépíteni, akár egy
programot. Először az alap dolgok működjenek, aztán jöhetnek a különböző
megszorítások:
- Legyen
egy program, ami számokat fogad el bemenetnek. Az ECMAScript a számokat a
[:digit:] vagy [:d:] szintaxissal azonosítja: regex r("[[:digit:]]"); - A
fenti kifejezés csak egyjegyű számokat fogad el. Több számjegy esetén a végére
kell írni egy + jelt: regex r("[[:digit:]]+"); - Ha
tesztelni szeretnénk, hogy a beolvasott szám nem-e negatív, akkor a kifejezés
elejébe a –? kombinációt kell tenni: regex r("-?[[:digit:]]+"); - Ha a
felhasználó netán a + karaktert is használja a számok előtt, akkor erre is fel
kell készíteni a fordítót, ugyanis ez egy speciális karakter az ECMAScript
szintaxisában. Hogy figyelmen kívül hagyja ezt a karaktert, a \\+? jelt kell a
kifejezés elejébe írni: regex r("(\\+?[[:digit:]]+"); - A
kifejezés előtt lévő ellenőrzéseke kombinálni kell egy „vagy” kapuval, mert
vagy az egyik, vagy a másik fordulhat elő: regex r("(\\+|-)?[[:digit:]]+");
Ezt a felépített kifejezést alkalmazzuk a
következő programban. Ha a bevitt számjegy nem felel meg a reguláris kifejezés
szabályainak, akkor a „Nem helyes!” üzenet jelenik meg. A program a q billentyű
beviteléig fut.
#include <iostream>
#include <regex>
#include <string>
using namespace std;
int main()
{
string input;
regex r("(\\+|-)?[[:digit:]]+");
while(true) //végtelen ciklus
{
cout
<< "Irj be egy egesz szamot: ";
cin >>
input;
if(input=="q")
break; // q esetén
kilép
if(regex_match(input,r))
cout
<< "Helyes!" << endl;
else
cout
<< "Nem helyes!" <<
endl;
}
}
A reguláris kifejezés helyességét az std::regex_match() függvény ellenőrzi. A kimenet:
Irj be egy egesz
szamot: 23
Helyes!
Irj be egy egesz
szamot: +51
Helyes!
Irj be egy egesz
szamot: -79
Helyes!
Irj be egy egesz
szamot: 6.7
Nem helyes!
Irj be egy egesz
szamot: +-41
Nem helyes!
Irj be egy egesz
szamot: abc
Nem helyes!
Irj be egy egesz
szamot: q
Ha valós számokra is hasonlóképp kell
felépíteni a reguláris kifejezést. Ott a tizedespont utáni rész opcionális,
ezért a ([[:digit:]]+)? kifejezéssel jelöljük. Ugyanígy a tizedespont is
opcionális ha nincs utána semmi, így az egész második részt (\\.)? közé lehet
tenni: regex rr("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?");
Ha a feladat, hogy tudományos formátumú
számokat lehessen beírni, mint pl. -1.23e+06 vagy 1E3, akkor azzal bővítjük az
előző kifejezést, hogy az exponenciális tag is opcionális részese lehet a
számnak. Az exponenciális részt a ((e|E)((\\+|-)?)[[:digit:]]+)? kifejezés
jelenti: regex rrr("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?((e|E)((\\+|-)?)[[:digit:]]+)?");
Az alapértelmezettként használt ECMAScript
logikája hamar elsajátítható, de beállítható más szintaxis is, például a POSIX
grep:
#include <iostream>
#include <string>
#include <regex>
int main()
{
std::string str = "zzxayyzz";
std::regex re1(".*(xay)"); //
ECMAScript
std::regex re2(".*(xay)", std::regex::grep); // POSIX grep
std::cout << "Kereses 'xay' utan az 'zzxayyzz' szovegben:\n";
std::smatch m;
std::regex_search(str, m, re1);
std::cout << " ECMAScript: " << m[0] << '\n';
std::regex_search(str, m, re2);
std::cout << "
POSIX grep: " << m[0] << '\n';
}
A fenti program egy keresést végez az str stringben. A keresést az std::regex_search() függvény végzi, melynek első paramétere egy string amiben a keresés
zajlik, a második egy konténer, ami a találatokat tartalmazza, a harmadik pedig
a keresés reguláris kifejezése. Az eredmény:
Kereses 'xayy'
utan az 'zzxayyzz' szovegben:
ECMAScript: zzxay
POSIX grep: zzxay
Raw stringek
A raw stringek olyan stringek, melyekben a
feloldójelek (pl. \n, \t, \) nem érvényesek. Szintaxisa: R”(szoveg)”. A
következő program jól szemlélteti a hagyományos és a raw string közti
különbséget:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string normal_str="Elso sor.\nMasodik sor.\nUzenet vege.\n";
string raw_str=R"(Elso sor.\nMasodik sor.\nUzenet vege.\n)";
cout<<normal_str<<endl;
cout<<raw_str<<endl;
return(0);
}
A kimenet:
Elso sor.
Masodik sor.
Uzenet vege.
Elso
sor.\nMasodik sor.\nUzenet vege.\n
A raw string egyik célja, hogy valamennyire
egyszerűsítse a reguláris kifejezések használatát. Ezzel közvetlen bevihetők az
ECMAScript kifejezések anélkül, hogy a feloldójeleket a fordító felismerné. Egy
korábbi példában a bemenő számok helyességét a következőképp ellenőriztük:
regex r("(\\+|-)?[[:digit:]]+");
Ugyanez raw stringgel:
regex integer(R"((\+|-)?[[:digit:]]+)");
Lambda függvények és
kifejezések
A reguláris kifejezések és a raw stringek
mellett egy másik hasznos nyelvezet a lambda. A lambda kifejezések lehetővé
teszik, hogy a név nélkül definiáljunk és használjunk függvényeket. Használható
a függvényobjektumok helyett, így nem kell külön osztályt és tagfüggvényt
definiálni. A következő lambda kifejezés egy olyan függvényt definiál, amelyik
egy számot térít vissza:
[]() -> int
{ return 4; }();
A fenti függvény igazából semmi hasznosat
nem tesz, csupán visszatéríti a 4-et, ami például kiírásra használható:
int result = []()
-> int { return
4; }();
cout << result << endl;
Kicsit hasznosabb a következő:
int result = [](int input) -> int
{ return 2 * input; }(10);
cout << result << endl;
A fenti kifejezésnek van agy bemenő paramétere
(input) és visszatéríti ennek dupláját. A függvény rögtön a deklarálás után
megkapja a 10 paramétert, így a kiírt érték 20. Hasonlóképp felírható két szám
összege:
int result = [](int a,int b) -> int { return a + b;
}(2,4);
cout << result << endl;
Akár a reguláris kifejezések, a lambda
kifejezések is kombinálhatók, egymásba építhetők:
int result = [](int m, int n) -> int { return m + n; }
([](int a) -> int
{ return a; }(2),[](int
a) -> int { return
a; }(3));
cout << result << endl;
A fenti lambda függvényben két másik lambda
függvény szerepel, egyik 2-t, másik 3-at térít vissza, a fő függvény pedig
összeadja ezeket, mint bemenő paramétereket. A függvénymutatók működnek a
lambda függvényekre is:
auto func = [](int a, int b) -> int { return a+b; };
cout << func(2, 3) << endl;
Az auto típus a func változót a lambda
kifejezésre mutató mutatóként fogja deklarálni. Tegyük fel, hogy egész
számokkal megpakolt vektor tartalmát kell rendezni a sort() algoritmussal. Ez a
hagyományos C++98 fordítóval a következőképp néz ki:
#include <iostream>
#include <vector>
#include <algorithm>
bool compare(int i,int j)
{
return (i<j);
}
using namespace std;
int main()
{
vector<int> v {3,1,7,4,8,9,5,6,2,10};
for(int i = 0; i <
v.size(); i++) cout << v[i] << "
"; cout << endl;
sort(v.begin(),
v.end(), compare); //rendezés
for(int i = 0; i <
v.size(); i++) cout << v[i] << "
"; cout << endl;
return 0;
}
Lambda kifejezéssel ugyanez elérhető,
megspórolva a compare() függvény deklarálását:
sort(v.begin(), v.end(), [](int i, int j) -> bool{ return (i <
j);}); //rendezés
Függvénymutatók
A függvénymutatók is a programozás
egyszerűsítésére szolgálnak. Egy függvény nem más, mint egy memóriacím, ahonnan
az utasítások kezdődnek. A függvény hívásához elegendő ezt a címet ismerni.
A következő függvénymutató olyan
függvényre mutat, melynek paramétere két char, visszatérített típusa pedig int. A mutató
neve func, amit bármilyen
függvényhez lehet használni, melynek ilyen a felépítése.
int (*func)(char,char) = NULL;
A függvénymutatók az osztályok
tagfüggvényeire is mutathatnak.
int (MyClass::*Classfunc)(char,char) = NULL;
Mindkét mutató NULL mutatónak van
inicializálva, de lehet rögtön a függvény címét is adni (pl. &fuggveny). Ha a mutató megkapta a függvény címét,
a függvény rajta keresztül hívható:
int result = func(10,2);
int result =
(obj.*Classfunc)(10,2);
Mivel mutatóról van szó, ez paraméterként
is átadható egy másik függvénynek.
void PassPtr(int (*funcptr)(char, char))
{
int result =
(*funcptr)(20,3); // call using function pointer
cout << "PassPtr:
" << result << endl;
}
A függvényt a következőképp lehet
meghívni: PassPtr(func);
A függvénymutató egy függvény
visszatérített értéke is lehet:
int
(*ReturnPtr(char muvelet))(char, char)
{
if(muvelet
== '+')
return
&Plus;
else
return
&Minus;
}
Ebben az esetben a Plus() és Minus() két hasonló függvény. A következő példában
összesítve vannak az eddig felsorolt példák:
#include <iostream>
using namespace std;
class MyClass
{
public:
int TagOsszeg(char a, char b) //tagfüggvény
{
return a+b;
}
int TagKulonbseg(char a, char b) //tagfüggvény
{
return a-b;
}
};
int Osszeg (char a, char b) //sima függvény
{
return a+b;
}
int Kulonbseg
(char a, char
b) //sima függvény
{
return a-b;
}
void PassPtr(int (*funcptr)(char, char))
{
int result =
(*funcptr)(20,3);
cout << "PassPtr(20,3):
" << result << endl;
}
int
(*ReturnPtr(char muvelet))(char, char)
{
if(muvelet
== '+')
return
&Osszeg;
else
return
&Kulonbseg;
}
int main()
{
int (*func)(char, char) = NULL; //függvénymutató egy sima függvényre
int (MyClass::*Classfunc)(char, char) = NULL; //függvénymutató a tagfüggvényre
int result;
func = &Osszeg;
result = func(12,3);
cout << "Osszeg(12,3):
" << result << endl;
func = &Kulonbseg;
result = (*func)(12,3);
cout << "Kulonbseg(12,3):
" << result << endl;
MyClass obj1;
Classfunc = &MyClass::TagOsszeg;
result = (obj1.*Classfunc)(10,2);
cout << "TagOsszeg(10,2):
" << result << endl;
MyClass *obj2 = new MyClass;
Classfunc = &MyClass::TagKulonbseg;
result = (obj2->*Classfunc)(10,2);
cout << "TagKulonbseg(10,2):
" << result << endl;
PassPtr(func);
result = (*ReturnPtr('+'))(3,7);
cout << "(*ReturnPtr('+'))(3,7):
" << result << endl;
return 0;
}
A kimenet:
Osszeg(12,3): 15
Kulonbseg(12,3):
9
TagOsszeg(10,2):
12
TagKulonbseg(10,2):
8
PassPtr(20,3):
17
(*ReturnPtr('+'))(3,7):
10
A függvénymutatók legnagyobb hasznát
igazából a tömbben való alkalmazáskor lehet érezni. Ilyenkor ugyanis a
függvényeket indexelni lehet.
#include <iostream>
using namespace std;
int Osszeg (char a, char b)
{
return a+b;
}
int Kulonbseg
(char a, char
b)
{
return a-b;
}
int Szorzat (char a, char b)
{
return a*b;
}
int Hatvany (char a, char b)
{
int h=1;
for(int
i=1; i<=b; i++)
h=h*a;
return h;
}
int main()
{
int
(*tomb[4])(char, char)
= {NULL}; //10 darab függvénymutató
tomb[0]=&Osszeg;
tomb[1]=&Kulonbseg;
tomb[2]=&Szorzat;
tomb[3]=&Hatvany;
for(int i=0; i<4; i++)
cout
<< tomb[i](6,2) << endl;
return 0;
}
A kimenet:
8
4
12
36
Ikuti AltairGate.com pada Aplikasi GOOGLE NEWS : FOLLOW (Dapatkan Berita Terupdate tentang Dunia Pendidikan dan Hiburan). Klik tanda ☆ (bintang) pada aplikasi GOOGLE NEWS.