C ++

Osnove regularnega izražanja v jeziku C ++

Osnove regularnega izražanja v jeziku C ++
Upoštevajte naslednji stavek v narekovajih:

"Tu je moj moški."

Ta niz je morda v računalniku in uporabnik bo morda želel vedeti, ali ima besedo "človek". Če ima besedo moški, bo morda želel besedo "moški" spremeniti v "ženska"; tako da naj se niz glasi:

"Tu je moja ženska."

Takšnih želja uporabnika računalnika je še veliko; nekateri so zapleteni. Redni izraz, okrajšan, regularni izraz, je predmet obravnavanja teh težav v računalniku. C ++ ima knjižnico z imenom regex. Torej, program C ++ za obdelavo regularnih izrazov se mora začeti z:

#include
#include
uporaba imenskega prostora std;

Ta članek razlaga osnove regularnega izražanja v jeziku C++.

Vsebina članka

  • Osnove rednega izražanja
  • Vzorec
  • Razredi znakov
  • Ujemanje presledkov
  • Obdobje (.) v vzorcu
  • Ujemanje ponovitev
  • Ujemajoča se alternacija
  • Ujemanje začetka ali konca
  • Združevanje v skupine
  • Stavki icase in več vrstic regex_constants
  • Ujemanje celotnega cilja
  • Predmet match_results
  • Položaj tekme
  • Poiščite in zamenjajte
  • Zaključek

Osnove rednega izražanja

Regex

Niz, kot je »Tu je moj človek.”Zgoraj je ciljno zaporedje ali ciljni niz ali preprosto cilj. "Moški", ki je bil iskan, je regularni izraz ali preprosto regex.

Ujemanje

Do ujemanja naj bi prišlo, ko se najde beseda ali besedna zveza, ki jo iščete. Po ujemanju lahko pride do zamenjave. Na primer, ko je »moški« nameščen zgoraj, ga lahko nadomesti z »ženska«.

Preprosto ujemanje

Naslednji program prikazuje, kako se ujema beseda "človek".

#include
#include
uporaba imenskega prostora std;
int main ()

regularni izraz reg ("človek");
if (regex_search ("Tukaj je moj človek.", reg))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;
vrnitev 0;

Funkcija regex_search () vrne true, če obstaja ujemanje, in vrne false, če ne pride do ujemanja. Tu ima funkcija dva argumenta: prvi je ciljni niz, drugi pa objekt regularnega izraza. Regex sam je "človek", z dvojnimi narekovaji. Prvi stavek v funkciji main () tvori objekt regularnega izraza. Redovni izraz je vrsta, reg pa objekt regularnega izraza. Izhodni podatki zgornjega programa se "ujemajo", saj je v ciljnem nizu viden "man". Če v cilju ni bilo videti "man", bi regex_search () vrnil false in izhod ne bi bil "ujemajoč".

Rezultat naslednje kode se "ne ujema":

regularni izraz reg ("človek");
if (regex_search ("Tukaj je moja izdelava.", reg))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Ni se ujemalo, ker regularnega izraza "man" ni bilo mogoče najti v celotnem ciljnem nizu, "Tukaj je moja izdelava."

Vzorec

Regularni izraz "človek" zgoraj je zelo preprost. Regeksi običajno niso tako preprosti. Regularni izrazi imajo metaznake. Metaznaki so znaki s posebnim pomenom. Metaznak je znak o znakih. Metaznaki regularnih izrazov C ++ so:

^ $ \ . * + ? () [] |

Redovni izraz z metaznaki ali brez njih je vzorec.

Razredi znakov

Kvadratni oklepaji

Vzorec ima lahko znake v oglatih oklepajih. S tem bi se določen položaj v ciljnem nizu ujemal s katerim koli znakom v oglatih oklepajih. Upoštevajte naslednje cilje:

"Mačka je v sobi."
"Netopir je v sobi."
"Podgana je v sobi."

Redovni izraz, [cbr] at bi se ujemal z mačko v prvi tarči. V drugi tarči bi se ujemal z netopirjem. V tretji tarči bi se ujemal s podgano. To je zato, ker se "mačka" ali "netopir" ali "podgana" začne s "c" ali "b" ali "r". To ponazarja naslednji segment kode:

regularni izraz reg ("[cbr] at");
if (regex_search ("Mačka je v sobi.", reg))
cout << "matched" << endl;
if (regex_search ("Netopir je v sobi.", reg))
cout << "matched" << endl;
if (regex_search ("Podgana je v sobi.", reg))
cout << "matched" << endl;

Rezultat je:

ujemajo
ujemajo
ujemajo

Razpon znakov

Razred, [cbr] v vzorcu [cbr], bi se ujemal z več možnimi znaki v cilju. V tarči bi se ujemal s 'c' ali 'b' ali 'r'. Če cilj nima nobenega od 'c' ali 'b' ali 'r', čemur sledi »at«, ne bi bilo ujemanja.

Nekatere možnosti, kot so 'c' ali 'b' ali 'r', obstajajo v območju. Razpon števk od 0 do 9 ima 10 možnosti in vzorec za to je [0-9]. Obseg malih abeced, od a do z, ima 26 možnosti, vzorec za to pa je [a-z]. Razpon velikih črk, od A do Ž, ima 26 možnosti, vzorec za to pa je [A-Z]. - ni uradno metaznak, vendar bi v oglatih oklepajih označeval obseg. Torej, naslednje ustvari ujemanje:

if (iskanje regularnih izrazov ("ID6id", regularni izraz ("[0-9]")))
cout << "matched" << endl;

Upoštevajte, kako je bil regularni izraz sestavljen kot drugi argument. Do ujemanja pride med števko, 6 v območju, od 0 do 9, in 6 v tarči, "ID6id". Zgornja koda je enakovredna:

if (iskanje regularnih izrazov ("ID6id", regularni izraz ("[0123456789]")))
cout << "matched" << endl;

Naslednja koda ustvari ujemanje:

char str [] = "ID6iE";
if (iskanje regularnih izrazov (str, regularni izraz ("[a-z]")))
cout << "matched" << endl;

Upoštevajte, da je tukaj prvi argument spremenljivka niza in ne niz literala. Ujemanje je med 'i' v [a-z] in 'i' v “ID6iE”.

Ne pozabite, da je obseg razred. Besedilo je lahko desno od obsega ali levo od obsega v vzorcu. Naslednja koda ustvari ujemanje:

if (regex_search ("ID2id je ID ", regularni izraz (" ID [0-9] id ")))
cout << "matched" << endl;

Ujemanje je med “ID [0-9] id” in “ID2id”. Preostali ciljni niz, »je ID«, se v tej situaciji ne ujema.

Kot uporabljen v temi regularnega izraza (regularni izrazi), beseda razred dejansko pomeni niz. To pomeni, da se mora eden od znakov v nizu ujemati.

Opomba: vezaj - je metaznak samo v oglatih oklepajih, ki označuje obseg. To ni metaznak v regularnem izrazu, zunaj oglatih oklepajev.

Negacija

Razred, ki vključuje obseg, je mogoče zanikati. To pomeni, da se ne smejo ujemati nobeni znaki v naboru (razredu). To je označeno z znakom ^ na začetku vzorca razreda, takoj za začetnim oklepajem. Torej, [^ 0-9] pomeni ujemanje znaka na ustreznem mestu v cilju, kar ni noben znak v obsegu, vključno z 0 do 9. Torej naslednja koda ne bo dala ujemanja:

if (iskanje regularnih izrazov ("0123456789101112", regularni izraz ("[^ 0-9]")))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Številko v območju od 0 do 9 lahko najdemo v katerem koli od ciljnih položajev niza, "0123456789101112,"; torej ni ujemanja - negacije.

Naslednja koda ustvari ujemanje:

če (iskanje regularnih izrazov ("ABCDEFGHIJ", regularni izraz ("[^ 0-9]")))
cout << "matched" << endl;

V cilju "ABCDEFGHIJ" ni bilo mogoče najti nobene številke; torej obstaja tekma.

[a-z] je obseg zunaj [^ a-z]. In tako je [^ a-z] negacija [a-z].

[A-Z] je obseg zunaj [^ A-Z]. In tako [^ A-Z] je negacija [A-Z].

Obstajajo tudi druge negacije.

Ujemanje presledkov

"ali \ t ali \ r ali \ n ali \ f je presledek. V naslednji kodi se regularni izraz "\ n" ujema z '\ n' v cilju:

if (regex_search ("Prve vrstice.\ r \ nDruge vrstice.", regularni izraz (" \ n ")))
cout << "matched" << endl;

Ujemanje katerega koli presledka

Vzorec ali razred, ki se ujema s katerim koli presledkom, je [\ t \ r \ n \ f]. V naslednji kodi se ujema:

if (iskanje regularnih izrazov ("ena dva", regularni izraz ("[\ t \ r \ n \ f]")))
cout << "matched" << endl;

Ujemanje katerega koli znaka, ki ni presledek

Vzorec ali razred, ki se ujema s katerim koli nebelim presledkom, je [^ \ t \ r \ n \ f]. Naslednja koda ustvari ujemanje, ker v cilju ni presledkov:

if (iskanje regularnih izrazov ("1234abcd", regularni izraz ("[^ \ t \ r \ n \ f]")))
cout << "matched" << endl;

Obdobje (.) v vzorcu

Obdobje (.) v vzorcu se ujema s katerim koli znakom, vključno s samim, razen \ n, v cilju. Ujema se v naslednji kodi:

if (regex_search ("1234abcd", regex (".")))
cout << "matched" << endl;

V naslednji kodi ni ustreznih rezultatov, ker je cilj "\ n".

if (iskanje regularnega izraza ("\ n", izraz regularnega izraza (".")))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Opomba: Tačka nima posebnega pomena v razredu znakov v oglatih oklepajih.

Ujemanje ponovitev

Znak ali skupina znakov se lahko v ciljnem nizu pojavi večkrat. Vzorec se lahko ujema s to ponovitvijo. Metaznaki, ?, *, + in se uporabljajo za ujemanje s ponovitvijo v cilju. Če je x znak, ki nas zanima v ciljnem nizu, imajo metaznaki naslednji pomen:

x *: pomeni ujemanje 'x' 0 ali več krat, tj.e., poljubno število krat
x +: pomeni ujemanje 'x' 1 ali večkrat, tj.e., vsaj enkrat
x? : pomeni ujemanje 'x' 0 ali 1-krat
x n,: pomeni, da se vsaj x ali večkrat ujema z 'x'. Upoštevajte vejico.
x n: natančno n krat se ujema z 'x'
x n, m: ujema se z 'x' vsaj n-krat, vendar ne več kot m-krat.

Ti metaznaki se imenujejo količniki.

Ilustracije

*

Znak * se nič ali večkrat ujema s predhodnim znakom ali prejšnjo skupino. “O *” se ujema z “o” v “dog” ciljnega niza. Prav tako se ujema z "oo" v "book" in "looking". Redovni izraz "o *" se ujema z "boooo" v "Žival booooed.". Opomba: “o *” se ujema z “dig”, kjer se za “o” pojavi nič (ali več) čas.

+

Znak + se 1 ali večkrat ujema s prejšnjim znakom ali prejšnjo skupino. Kontrastirajte z nič ali večkrat za *. Torej se regularni izraz "e +" ujema z "e" v "jesti", kjer se "e" pojavi enkrat. "E +" se ujema tudi z "ee" v "ovaca", kjer se "e" pojavlja večkrat. Opomba: "e +" se ne bo ujemalo z "dig", ker se v "dig" "e" ne pojavi vsaj enkrat.

?

The ? se ujema s prejšnjim znakom ali prejšnjo skupino, 0 ali 1-krat (in ne več). Torej, »npr?"Se ujema z" dig ", ker se" e "pojavlja v" dig ", nič časa. „E?”Se ujema z“ set ”, ker se e enkrat pojavi v“ set ”. Opomba: „e?"Še vedno ustreza" ovca "; čeprav sta dva "e" v "ovcah". Tu je odtenek - glej kasneje.

n,

Ta se ujema z vsaj n zaporednimi ponovitvami predhodnega znaka ali predhodne skupine. Torej se regularni izraz "e 2," ujema z dvema "e" v cilju, "ovca" in tremi "e" v ciljni "ovci". "E 2," se ne ujema z "set", ker ima "set" samo eno "e".

n

To se ujema z natančno n zaporednimi ponovitvami prejšnjega znaka ali predhodne skupine. Torej se regularni izraz "e 2" ujema z dvema "e" v cilju, "ovaca". "E 2" se ne ujema z "set", ker ima "set" samo eno "e". No, "e 2" se ujema z dvema e v tarči, "ovca". Tu je odtenek - glej kasneje.

n, m

Ta se ujema z več zaporednimi ponovitvami predhodnega znaka ali predhodne skupine, kjer koli od n do vključno m. Torej, "e 1,3" se v "dig" ne ujema z nič, kar nima "e". Ujema se z enim 'e' v 'set', dve 'e' v 'ovaca', tri 'e' v 'sheeep' in tri 'e' in 'sheeeep'. Na zadnji tekmi je odtenek - glej kasneje.

Ujemanje alternacij

Razmislite o naslednjem ciljnem nizu v računalniku.

»Na kmetiji so prašiči različnih velikosti."

Programer bi morda želel vedeti, ali ima ta tarča "kozo", "zajca" ali "prašiča". Koda bi bila naslednja:

char str [] = "Na kmetiji so prašiči različnih velikosti.";
if (iskanje regularnih izrazov (str, regularni izraz ("koza | zajec | prašič))))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Koda ustvari ujemanje. Upoštevajte uporabo znaka izmene, |. Možnosti so lahko dve, tri, štiri in več. C ++ bo najprej poskušal najti prvo alternativo, "kozo", na vsakem položaju znaka v ciljnem nizu. Če s "kozo" ne uspe, poskusi z naslednjo alternativo, "zajec". Če z "zajcem" ne uspe, poskusi z naslednjo alternativo, "prašič". Če »prašič« ne uspe, se C ++ premakne na naslednji položaj v cilju in znova začne s prvo alternativo.

V zgornji kodi se ujema »prašič«.

Ujemanje začetka ali konca

Začetek


Če je ^ na začetku regularnega izraza, se lahko z začetnim besedilom ciljnega niza ujema z regularnim izrazom. V naslednji kodi je začetek cilja "abc", ki se ujema:

if (iskanje regularnih izrazov ("abc in def", regularni izraz ("^ abc")))
cout << "matched" << endl;

V naslednji kodi ni ujemanja:

if (iskanje regularnih izrazov ("Da, abc in def", regularni izraz ("^ abc")))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Tu "abc" ni na začetku cilja.

Opomba: Znak cirkumfleksa, '^', je metaznak na začetku regularnega izraza, ki se ujema z začetkom ciljnega niza. Še vedno je metaznak na začetku razreda znakov, kjer negira razred.

Konec

Če je $ na koncu regularnega izraza, lahko regularni izraz ujema s končnim besedilom ciljnega niza. V naslednji kodi je konec cilja »xyz«, ki se ujema:

if (iskanje regularnih izrazov ("uvw in xyz", regularni izraz ("xyz $")))
cout << "matched" << endl;

V naslednji kodi ni ujemanja:

if (iskanje regularnih izrazov ("uvw in xyz final", regularni izraz ("xyz $")))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Tu "xyz" ni na koncu cilja.

Združevanje v skupine

V oklepajih lahko združimo znake v vzorec. Razmislite o naslednjem regularnem izrazu:

"koncert (pianist)"

Skupina tukaj je "pianistka", obdana z metaznaki (in). Pravzaprav gre za podskupino, medtem ko je "koncert (pianist)" celotna skupina. Upoštevajte naslednje:

"(Pianist je dober)"

Tu je podskupina ali podniz »pianist dober«.

Podnizi s skupnimi deli

Knjigovodja je oseba, ki skrbi za knjige. Predstavljajte si knjižnico z knjigovodjo in knjižno polico. Predpostavimo, da je v računalniku eden od naslednjih ciljnih nizov:

"Knjižnica ima knjižno polico, ki jo občudujejo.";
"Tu je knjigovodja.";
"Knjigovodja dela s knjižno polico.";

Predpostavimo, da programerja ne zanima, kateri od teh stavkov je v računalniku. Kljub temu ga zanima, ali je "knjižna polica" ali "knjigovodja" prisotna v katerem koli ciljnem nizu v računalniku. V tem primeru je lahko njegov regularni izraz:

"knjižna polica | knjigovodja."

Uporaba izmeničnega toka.

Opazite, da je bila "knjiga", ki je skupna obema besedama, vtipkani dvakrat, in sicer v obe besedi v vzorcu. Da bi se izognili dvakratnemu tipkanju »book«, bi bil regularni izraz bolje napisati kot:

"knjiga (polica | imetnik)"

Tukaj je skupina, "polic | čuvaj". Metaznak izmenjave je še vedno uporabljen, vendar ne za dve dolgi besedi. Uporabljen je bil za dva končna dela dveh dolgih besed. C ++ obravnava skupino kot entiteto. Torej, C ++ bo poiskal "polico" ali "imetnika", ki pride takoj za "knjigo". Rezultat naslednje kode se "ujema":

char str [] = "Knjižnica ima knjižno polico, ki jo občudujejo.";
if (regex_search (str, regex ("knjiga (polica | skrbnik)")))
cout << "matched" << endl;

„Knjižna polica“ in ne „knjigovodja“ sta bila ujeta.

Stavki icase in več vrstic regex_constants

icase

Ujemanje privzeto razlikuje med velikimi in malimi črkami. Vendar pa lahko postane neobčutljiv na velike in male črke. Da bi to dosegli, uporabite konstanto regex :: icase, kot v naslednji kodi:

if (regex_search ("Feedback", regex ("feed", regex :: icase)))
cout << "matched" << endl;

Izhod je "usklajen". Torej, »Feedback« z velikimi črkami »F« se ujema z »feed« z malimi črkami »f«. “Regex :: icase” je bil drugi argument konstruktorja regex (). Brez tega izjava ne bi povzročila ujemanja.

Multiline

Upoštevajte naslednjo kodo:

char str [] = "vrstica 1 \ n vrstica 2 \ n vrstica 3";
if (regex_search (str, regex ("^.* $ ")))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Izhod je "ni enak". Redovni izraz "^.* $, «Se ujema s ciljnim nizom od začetka do konca. “.* ”Pomeni kateri koli znak, razen \ n, nič ali večkrat. Torej, zaradi znakov za novo vrstico (\ n) v cilju ni bilo nobenega ujemanja.

Cilj je večvrstični niz. Da bi.', da se ujema z znakom nove vrstice, je treba narediti konstanto "regex :: multiline", drugi argument konstrukcije regex (). Naslednja koda to ponazarja:

char str [] = "vrstica 1 \ n vrstica 2 \ n vrstica 3";
if (regex_search (str, regex ("^.* $ ", regularni izraz: večvrstični))))
cout << "matched" << endl;
drugače
cout << "not matched" << endl;

Ujemanje celotnega ciljnega niza

Za ujemanje s celotnim ciljnim nizom, ki nima znaka nove vrstice (\ n), lahko uporabimo funkcijo regex_match (). Ta funkcija se razlikuje od regex_search (). Naslednja koda to ponazarja:

char str [] = "prva druga tretjina";
if (regex_match (str, regex (".* drugo.* ")))
cout << "matched" << endl;

Tu je tekma. Vendar upoštevajte, da se regularni izraz ujema s celotnim ciljnim nizom in ciljni niz nima nobenega \ \ n.

Predmet match_results

Funkcija regex_search () lahko sprejme argument med ciljem in objektom regularnega izraza. Ta argument je objekt match_results. Z njim lahko poznamo celoten usklajeni niz (del) in ujemajoče se nize. Ta objekt je posebna matrika z metodami. Tip predmeta match_results je cmatch (za nizovne literale).

Pridobivanje tekem

Upoštevajte naslednjo kodo:

char str [] = "Ženska, ki ste jo iskali!";
primerjava m;
if (regex_search (str, m, regex ("w.m.n ")))
cout << m[0] << endl;

Ciljni niz ima besedo "ženska". Rezultat je „ženska“, kar ustreza regularnemu izrazu, „w.m.n ". Pri indeksu nič ima posebna matrika edino ujemanje, in sicer "ženska".

Pri možnostih razreda se v posebno matriko pošlje samo prvi podniz, ki ga najdemo v cilju. Naslednja koda to ponazarja:

primerjava m;
if (regex_search ("Podgana, mačka, netopir!", m, regularni izraz (" [bcr] at ")))
cout << m[0] << endl;
cout << m[1] << endl;
cout << m[2] << endl;

Rezultat je "podgana" iz indeksa nič. m [1] in m [2] sta prazni.

Pri drugih možnostih se v posebno polje pošlje samo prvi podniz, ki ga najdemo v cilju. Naslednja koda to ponazarja:

if (regex_search ("zajec, koza, prašič!", m, regularni izraz (" koza | zajec | prašič ")))
cout << m[0] << endl;
cout << m[1] << endl;
cout << m[2] << endl;

Rezultat je "zajec" iz indeksa nič. m [1] in m [2] sta prazni.

Skupine

Ko so vključene skupine, se celoten vzorec, ki se ujema, premakne v nič celico posebnega polja. Naslednji najdeni podniz gre v celico 1; podniz, ki sledi, gre v celico 2; in tako naprej. Naslednja koda to ponazarja:

if (regex_search ("Najboljša knjigarna danes!", m, regularni izraz (" book ((sel) (ler)) ")))
cout << m[0] << endl;
cout << m[1] << endl;
cout << m[2] << endl;
cout << m[3] << endl;

Rezultat je:

prodajalec knjig
prodajalec
sel
ler

Upoštevajte, da skupina (prodajalec) pride pred skupino (sel).

Položaj tekme

Položaj ujemanja za vsak podniz v matrični matriki je lahko znan. Štetje se začne od prvega znaka ciljnega niza, na položaju nič. Naslednja koda to ponazarja:

primerjava m;
if (regex_search ("Najboljša knjigarna danes!", m, regularni izraz (" book ((sel) (ler)) ")))
cout << m[0] << "->" << m.position(0) << endl;
cout << m[1] << "->" << m.position(1) << endl;
cout << m[2] << "->" << m.position(2) << endl;
cout << m[3] << "->" << m.position(3) << endl;

Kot argument upoštevajte uporabo lastnosti položaja z indeksom celice. Rezultat je:

prodajalec knjig-> 5
prodajalec-> 9
sel-> 9
ler-> 12

Poiščite in zamenjajte

Ujemanje lahko nadomesti nova beseda ali besedna zveza. Za to se uporablja funkcija regex_replace (). Vendar je tokrat niz, pri katerem pride do zamenjave, nizni objekt in ne literal niza. Torej, knjižnica nizov mora biti vključena v program. Ilustracija:

#include
#include
#include
uporaba imenskega prostora std;
int main ()

string str = "Tukaj prihaja moj mož. Gre tvoj mož.";
string newStr = regex_replace (str, regex ("moški"), "ženska");
cout << newStr << endl;
vrnitev 0;

Funkcija regex_replace (), kot je tu kodirana, nadomešča vsa ujemanja. Prvi argument funkcije je cilj, drugi je objekt regularnega izraza in tretji nadomestni niz. Funkcija vrne nov niz, ki je ciljni, vendar nadomestni. Rezultat je:

»Prihaja moja ženska. Tu gre tvoja ženska."

Zaključek

Regularni izraz uporablja vzorce za ujemanje podnizov v ciljnem nizu zaporedja. Vzorci imajo metaznake. Pogosto uporabljene funkcije za regularne izraze C ++ so: regex_search (), regex_match () in regex_replace (). Redovni izraz je vzorec v dvojnih narekovajih. Vendar te funkcije argumentirajo objekt regularnega izraza in ne samo regularnega izraza. Redovni izraz je treba narediti v objekt regularnega izraza, preden ga te funkcije lahko uporabijo.

OpenTTD vs Simutrans
Ustvarjanje lastne simulacije prevoza je lahko zabavno, sproščujoče in izjemno vabljivo. Zato morate preizkusiti čim več iger, da boste našli tisto, k...
Vadnica za OpenTTD
OpenTTD je ena izmed najbolj priljubljenih poslovnih simulacijskih iger. V tej igri morate ustvariti čudovit prevozniški posel. Vendar boste začeli na...
SuperTuxKart za Linux
SuperTuxKart je odličen naslov, zasnovan tako, da vam brezplačno ponuja izkušnjo Mario Kart v vašem sistemu Linux. Igrati je precej zahtevno in zabavn...