5. gyakorlat
Az itteni regex motor is támogatja többek közt ezeket a műveleteket:
- illeszkedik-e egy teljes input szöveg egy regexre (match bool), a
std::regex_match
függvénnyel
- illeszkedés esetén capturing groupok tartalmának kigyűjtésére (match groups), a
regex_match
nek megadva egy std::smatch
objektumot
is, melybe az illeszkedés részletei kerülnek, pl. a csoportok;
- input szövegben az input regexre első illeszkedő substring megkereresése (search), a
std::regex_search
függvénnyel
- input szövegben az input regexre illeszkedő substringek, vagy közülük csak az első cseréje valamire (replaceAll, replace),
a
std::regex_replace
függvénnyel
A lenti példák egyben itt vannak.
match
Lássunk egy egyszerű "illik-e vagy sem" kódot: megkeressük (ggmarkk után szabadon), hogy az input
stringünkben vajon szerepel-e (külön szóként) a "telefon" szó.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | #include <regex> // std::regex osztály, std::regex_match függvény, C++11 óta része
#include <string> // std::string
#include <iostream> // std::cout, std::endl
// using namespace std; // ízlés dolga, hogy ki szereti behúzni az egész névteret
// using std::string; // vagy csak egy-egy osztályt belőle
// vagy kiírja mindig, hogy std::string, string helyett
...
// arra a stringekre illik, melyek tartalmazzák a "telefon" szót
std::regex theRegex(".*\\btelefon\\b.*");
// c++ alapból használjunk std::stringet, ha lehet
std::string theText = "alma körte telefon barack";
if( std::regex_match( // regex_match: bool, illik-e
theText, // erre a std::string vagy char* szövegre
theRegex ) ) // ez a std::regex
{
std::cout << "a regex illik a szövegre" << std::endl;
} else {
std::cout << "a regex NEM illik a szövegre" << std::endl;
}
|
groups
Ha az illesztést úgy szeretnénk elvégezni, hogy utána a capturing groupokkal is szeretnénk kezdeni valamit,
adnunk kell egy std::regex_match
referenciát a regex_match
nek, második argumentumként.
Ez egy template osztály, ugyanazzal a karaktertípussal kell példányosítsuk, mint ami az input text stringnek
a karaktertípusa -- de két példányosztály aliasolva van nekünk előre: ha char *
a textünk,
akkor az std::cmatch
osztályt, ha pedig string
, akkor az std::smatch
osztályt használhatjuk.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | // C++ban megadhatunk R"( ... )" delimeterek közt "raw string literálokat" is,
// ezeken belül nincs escapelés, így nem kell \\ -elnünk, mint Javában kellett
std::regex theRegex( R"(\s*((?:\S+(?:\s+\S+)*)?)\s*)" );
string theText = " a whitespace duplaplusz nemjó ";
// ebbe fognak kerülni a csoportok
std::smatch results;
// text, match, regex a sorrend, ha illik, akkor feltölti a match-be a csoportokat
if( std::regex_match( theText, results, theRegex ) )
{
// a match olyan kb. mint egy vector: van neki mérete
cout << "we have a match! Groups count: " << results.size() << endl;
for( int i = 0; i < results.size(); i++ )
{
// és tömbelem-kiválasztás [] operátora is, visszaadja az i. csoportot
cout << "Group " << i << " is *" << results[i] << "*" << endl;
}
} else {
cerr << "Sumthin is wrong"; //should never happen, ez a regex mindenre illeszkedik
}
|
Ennek a fenti hívásnak az eredményeképp megkapjuk, hogy két csoport van, a nulladikba került a teljes string,
az elsőbe pedig a a whitespace duplaplusz nemjó
string.
Ha pedig valamiért char*
az input:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | std::regex theRegex( R"(\s*((?:\S+(?:\s+\S+)*)?)\s*)" );
// most char* a text
char* theText = " a whitespace duplaplusz nemjó ";
//ezért cmatch-ot használunk
std::cmatch results;
if( std::regex_match( theText, results, theRegex ) )
{
cout << "we have a match! Groups count: " << results.size() << endl;
for( int i = 0; i < results.size(); i++ )
{
cout << "Group " << i << " is *" << results[i] << "*" << endl;
}
} else {
cerr << "Sumthin is wrong"; //should never happen, ez a regex mindenre illeszkedik
}
|
Az output persze ugyanaz, mint volt az előbb is.
search
Ha regex_match
helyett a regex_search
függvényt hívjuk, ez szintén egy bool
t ad vissza,
hogy van-e az input textben az input regexre illeszkedő substring; ha igen, akkor az első
illeszkedő substringgel frissíti a paraméterként kapott match_result
ot, ha kapott olyat:
| regex theRegex( "[+-]?\\d+" );
string theText( "Ebben a szövegben 3 szám és 12 szó van, vagy még több" );
std::smatch results;
if( std::regex_search( theText, results, theRegex ) )
{
cout << "Egy szám: " << results[0] << endl; // prints '3'
}
|
Ha szeretnénk olyan funkcionalitást, amilyen pl. a grep
nek van és megkeresni az összes illeszkedő
stringet, akkor pl. iterálhatjuk a regex_search
et úgy, hogy minden lépésben az input stringnek a
match utáni részére hívjuk (tehát ez nem olyan, mint a Java find(int i)
metódusa).
Ezt a stringet állítja nekünk elő a match_results
osztály suffix()
függvénye:
| regex theRegex( "[+-]?\\d+" );
string theText( "Ebben a szövegben 3 szám és 12 szó van, vagy -1 több" );
std::smatch results;
string tempString = theText; // ne rontsuk szét az eredeti szöveget
while( std::regex_search( tempString, results, theRegex ) ) {
cout << "Egy szám: " << results[0] << endl; //prints '3', then prints '12', then prints -1
tempString = results.suffix();
}
|
Érdemes lehet tudni, hogy a fenti kód nem készít új másolatot a suffix()
metódus hívásakor, az így készült
tempString
ugyanannak stringnek a memóriaterületére fog mutatni. Maga a suffix()
metódus nem is egy string
et
ad vissza, hanem egy referenciát egy olyan objektumra, melyből pl. az értékadás operátorral egy string
objektumot
tud készíteni, de másolás nélkül.
replace
A csomag sed
-like regex alapú search&replace allt is támogat a std::regex_replace
függvényen keresztül.
Ennek értelemszerű használatára egy példa:
| regex theRegex( "[+-]?\\d+" ); // megint számjegyek, possibly előjellel
string theText = "Ebben a szövegben a 27 és a -42 fordulnak elő számként.";
string replaced = std::regex_replace(theText, theRegex, "SZÁM");
cout << replaced << endl; // prints Ebben a szövegben a SZÁM és a SZÁM fordulnak elő számként.
|
Ha nem pont ez a szándékunk, akkor flagekkel
módosítható a működés: pl. a format_first_only
flag beállításával csak az első előfordulást cseréljük:
| regex theRegex("[+-]?\\d+"); //megint számjegyek, possibly előjellel, két csoportban
string theText = "Ebben a szövegben a 27 és a -42 fordulnak elő számként.";
string replaced = std::regex_replace(
theText, // miben cseréljen
theRegex, // a regexre illőt
"SZÁM", // mire cseréljen
std::regex_constants::format_first_only ); // flagek, logikai vagyolva
cout << replaced << endl; // prints Ebben a szövegben a SZÁM és a -42 fordulnak elő számként.
|
A string, melyre cserélünk, tartalmazhatja még a következőket:
- a
$&
jelzi magát a matchelt substringet
- a $` jelzi a stringnek a match előtti részét (note: replaceAll hívásakor ez az előző match és az aktuális match
közti rész lesz)
- a
$'
a stringnek a match utáni részét (note: ez aposztróf, az előző meg backtick)
- a
$0
, $1
, stb. pedig a nulladik, első stb. capturing group tartalmát.
Így pl. ez a kód zárójelbe teszi az input szövegben a számok előjel utáni részét:
| regex theRegex( "([+-]?)(\\d+)" );
string theText = "Ebben a szövegben a 27 és a -42 fordulnak elő számként.";
string replaced = std::regex_replace( theText, theRegex, "$1($2)" );
cout << replaced << endl; // prints Ebben a szövegben a (27) és a -(42) fordulnak elő számként.
|
Feladatok
- Írjunk regexet, mely pontosan akkor illeszkedik egy input sorra, ha benne páros sok
a
betű szerepel. (Ehhez talán először rajzoljunk egy automatát,
aztán ezt konvertáljuk algoritmussal regexbe.)
- Írjunk regexet, mely pontosan akkor illeszkedik egy input sorra, ha benne páros sok
a
betű vagy páratlan sok b
betű szerepel.
- Írjunk regexet, mely pontosan akkor illeszkedik egy input sorra, ha benne páros sok
a
betű és páratlan sok b
betű szerepel.
- Az előzőekben megadott páros-páratlan regexek illesztési sebességét mérjük ki Javában és egreppel is oly módon, hogy
- generáljunk egy nagy szöveges filet, sok hosszú sorral, melyekben random a, b és c betűk vannak
- futtassuk le az illesztést a regexünkkel, melyet készítettünk
- futtassunk le egy olyan programot, mely két egyszerűbb regex egymás utáni illesztésével végzi el a feladatot
- vonjunk le tanulságot!
Utolsó frissítés: 2021-10-23 18:39:09