C++

Basisprincipes van reguliere expressies in C++

Basisprincipes van reguliere expressies in C++
Beschouw de volgende zin tussen aanhalingstekens:

"Hier is mijn man."

Deze tekenreeks bevindt zich mogelijk in de computer en de gebruiker wil misschien weten of deze het woord "man" bevat. Als het het woord man heeft, wil hij misschien het woord "man" veranderen in "vrouw"; zodat de string zou moeten lezen:

"Hier is mijn vrouw."

Er zijn nog veel meer van dit soort verlangens van de computergebruiker; sommige zijn complex. Reguliere expressie, afgekort, regex, is het onderwerp van de behandeling van deze problemen door de computer. C++ wordt geleverd met een bibliotheek genaamd regex. Dus een C++-programma om met regex om te gaan, zou moeten beginnen met:

#include
#include
namespace std; gebruiken;

Dit artikel legt de basisprincipes van reguliere expressies uit in C++.

Artikel Inhoud

  • Basisprincipes van reguliere expressies
  • Patroon
  • Karakter klassen
  • Overeenkomende witruimtes
  • De periode (.) in het patroon
  • Overeenkomende herhalingen
  • Bijpassende afwisseling
  • Overeenkomend begin of einde
  • Groepering
  • De icase en multiline regex_constants
  • Overeenkomen met het hele doel
  • Het match_results-object
  • Positie van wedstrijd
  • Zoeken en vervangen
  • Conclusie

Basisprincipes van reguliere expressies

Regex

Een string als "Hier is mijn man".” hierboven is de doelreeks of doelreeks of eenvoudigweg, doel. "man", waarnaar werd gezocht, is de reguliere expressie, of eenvoudigweg, regex.

Bij elkaar passen

Er wordt gezegd dat er sprake is van matching wanneer het woord of de zin waarnaar wordt gezocht, wordt gevonden. Na matching kan er een vervanging plaatsvinden. Nadat bijvoorbeeld "man" hierboven staat, kan het worden vervangen door "vrouw".

Eenvoudige Matching

Het volgende programma laat zien hoe het woord "man" wordt gevonden.

#include
#include
namespace std; gebruiken;
int hoofd()

regex reg("man");
if (regex_search("Hier is mijn man.", reg))
cout << "matched" << endl;
anders
cout << "not matched" << endl;
retourneer 0;

De functie regex_search() retourneert true als er een overeenkomst is en retourneert false als er geen overeenkomst is. Hier heeft de functie twee argumenten: de eerste is de doelreeks en de tweede is het regex-object. De regex zelf is "man", tussen dubbele aanhalingstekens. De eerste instructie in de functie main() vormt het regex-object. Regex is een type en reg is het regex-object. De uitvoer van het bovenstaande programma is "matched", zoals "man" wordt gezien in de doelstring. Als "man" niet werd gezien in het doel, zou regex_search() false hebben geretourneerd en zou de uitvoer "niet overeenkomen" zijn geweest.

De uitvoer van de volgende code is "niet overeenkomend":

regex reg("man");
if (regex_search("Hier is mijn werk.", reg))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

Niet gevonden omdat de regex "man" niet gevonden kon worden in de hele doelstring, "Hier is mijn maaksel"."

Patroon

De reguliere expressie, "man" hierboven, is heel eenvoudig. Regexes zijn meestal niet zo eenvoudig. Reguliere expressies hebben metatekens. Metatekens zijn tekens met een speciale betekenis. Een metakarakter is een karakter over karakters. C++ regex-metatekens zijn:

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

Een regex, met of zonder metatekens, is een patroon.

Karakter klassen

Vierkante haakjes

Een patroon kan tekens tussen vierkante haken bevatten. Hiermee zou een bepaalde positie in de doelreeks overeenkomen met de tekens van de vierkante haken. Denk aan de volgende doelen:

"De kat is in de kamer."
"De vleermuis is in de kamer."
"De rat is in de kamer."

De regex, [cbr]at zou overeenkomen met kat in het eerste doel. Het zou overeenkomen met de vleermuis in het tweede doel. Het zou overeenkomen met rat in het derde doelwit. Dit komt omdat "kat" of "vleermuis" of "rat" begint met 'c' of 'b' of 'r'. Het volgende codesegment illustreert dit:

regex reg("[cbr]at");
if (regex_search("De kat is in de kamer.", reg))
cout << "matched" << endl;
if (regex_search("De vleermuis is in de kamer.", reg))
cout << "matched" << endl;
if (regex_search("De rat is in de kamer.", reg))
cout << "matched" << endl;

De uitvoer is:

op elkaar afgestemd
op elkaar afgestemd
op elkaar afgestemd

Bereik van tekens

De klasse, [cbr] in het patroon [cbr], zou overeenkomen met verschillende mogelijke tekens in het doel. Het zou overeenkomen met 'c' of 'b' of 'r' in het doel. Als het doelwit geen 'c' of 'b' of 'r' heeft, gevolgd door 'at', zou er geen overeenkomst zijn.

Sommige mogelijkheden zoals 'c' of 'b' of 'r' bestaan ​​in een bereik. Het bereik van cijfers, 0 tot 9 heeft 10 mogelijkheden, en het patroon daarvoor is [0-9]. Het bereik van kleine letters, a tot z, heeft 26 mogelijkheden, en het patroon daarvoor is [a-z]. Het bereik van hoofdletters, A tot Z, heeft 26 mogelijkheden, en het patroon daarvoor is [A-Z]. - is officieel geen metateken, maar tussen vierkante haken zou het een bereik aangeven. Het volgende levert dus een match op:

if (regex_search("ID6id", regex("[0-9]")))
cout << "matched" << endl;

Merk op hoe de regex is geconstrueerd als het tweede argument. De overeenkomst vindt plaats tussen het cijfer, 6 in het bereik, 0 tot 9, en de 6 in het doel, "ID6id". De bovenstaande code is gelijk aan:

if (regex_search("ID6id", regex("[0123456789]")))
cout << "matched" << endl;

De volgende code levert een overeenkomst op:

char str[] = "ID6iE";
if (regex_search(str, regex("[a-z]")))
cout << "matched" << endl;

Merk op dat het eerste argument hier een stringvariabele is en niet de letterlijke string. De match is tussen 'i' in [a-z] en 'i' in "ID6iE".

Vergeet niet dat een bereik een klasse is. Er kan tekst rechts van het bereik of links van het bereik in het patroon staan. De volgende code levert een overeenkomst op:

if (regex_search("ID2id is een ID", regex("ID[0-9]id")))
cout << "matched" << endl;

De overeenkomst is tussen "ID[0-9]id" en "ID2id". De rest van de doelreeks, "is een ID", komt in deze situatie niet overeen.

Zoals gebruikt in het reguliere expressie-onderwerp (regexes), betekent het woord klasse eigenlijk een set. Dat wil zeggen, een van de karakters in de set moet overeenkomen.

Opmerking: het koppelteken - is alleen een metateken tussen vierkante haken en geeft een bereik aan. Het is geen metateken in de regex, buiten de vierkante haken.

Negatie

Een klasse met een bereik kan worden genegeerd. Dat wil zeggen, geen van de tekens in de set (klasse) mag overeenkomen. Dit wordt aangegeven met het metateken ^ aan het begin van het klassenpatroon, net na de opening vierkante haak. Dus [^0-9] betekent overeenkomen met het teken op de juiste positie in het doel, wat geen teken in het bereik is, inclusief 0 tot en met 9. Dus de volgende code zal geen overeenkomst opleveren:

if (regex_search("0123456789101112", regex("[^0-9]")))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

Een cijfer binnen het bereik van 0 tot 9 kan worden gevonden in een van de doeltekenreeksposities, "0123456789101112,"; dus er is geen overeenkomst - ontkenning.

De volgende code levert een overeenkomst op:

if (regex_search("ABCDEFGHIJ", regex("[^0-9]")))
cout << "matched" << endl;

Er kon geen cijfer worden gevonden in het doel, "ABCDEFGHIJ",; dus er is een match.

[a-z] is een bereik buiten [^a-z]. En dus [^a-z] is de ontkenning van [a-z].

[A-Z] is een bereik buiten [^A-Z]. En dus [^A-Z] is de ontkenning van [A-Z].

Er zijn andere ontkenningen.

Overeenkomende witruimtes

"of \t of \r of \n of \f is een spatieteken. In de volgende code komt de regex, "\n" overeen met '\n' in het doel:

if (regex_search("Van regel één.\r\nVan regel twee.", regex("\n")))
cout << "matched" << endl;

Overeenkomen met elk witruimteteken

Het patroon of de klasse die overeenkomt met een witruimteteken is, [ \t\r\n\f]. In de volgende code, "komt overeen:

if (regex_search("een twee", regex("[ \t\r\n\f]")))
cout << "matched" << endl;

Overeenkomen met elk niet-witruimteteken

Het patroon of de klasse die overeenkomt met elk niet-witruimteteken is, [^ \t\r\n\f]. De volgende code produceert een overeenkomst omdat er geen witruimte in het doel is:

if (regex_search("1234abcd", regex("[^ \t\r\n\f]")))
cout << "matched" << endl;

De periode (.) in het patroon

De periode (.) in het patroon komt overeen met elk teken, inclusief zichzelf, behalve \n, in het doel. Een match wordt geproduceerd in de volgende code:

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

Geen overeenkomende resultaten in de volgende code omdat het doel "\n" is.

if (regex_search("\n", regex(".")))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

Opmerking: binnen een tekenklasse met vierkante haken heeft de punt geen speciale betekenis.

Overeenkomende herhalingen

Een teken of een groep tekens kan meer dan één keer voorkomen binnen de doelreeks. Een patroon kan deze herhaling evenaren. de metakarakters, ?, *, + en worden gebruikt om de herhaling in het doel te matchen. Als x een interessant teken is in de doelreeks, hebben de metatekens de volgende betekenis:

x*: betekent match 'x' 0 of meer keer, i.e., een willekeurig aantal keren
x+: betekent match 'x' 1 of meer keer, i.e., ten minste een keer
X? : betekent match 'x' 0 of 1 keer
xn,: betekent dat 'x' minstens n of meer keer overeenkomt. Let op de komma.
xn : match 'x' exact n keer
xn,m: match 'x' minstens n keer, maar niet meer dan m keer.

Deze metatekens worden kwantoren genoemd.

Illustraties

*

De * komt overeen met het voorgaande teken of de voorgaande groep, nul of meer keer. "o*" komt overeen met 'o' in "hond" van de doelreeks. Het komt ook overeen met "oo" in "boek" en "kijken". De regex, "o*" komt overeen met "boooo" in "The animal booooed.”. Opmerking: "o*" komt overeen met "dig", waarbij 'o' nul (of meer) tijd voorkomt.

+

De + komt overeen met het voorgaande teken of de voorgaande groep, 1 of meerdere keren. Vergelijk het met nul of meer keer voor *. Dus de regex, "e+" komt overeen met 'e' in "eat", waarbij 'e' één keer voorkomt. "e+" komt ook overeen met "ee" in "schapen", waarbij 'e' meer dan één keer voorkomt. Opmerking: "e+" komt niet overeen met "dig" omdat in "dig" 'e' niet minstens één keer voorkomt.

?

De ? komt overeen met het voorgaande teken of de voorgaande groep, 0 of 1 keer (en niet meer). Dus, "e?" komt overeen met "dig" omdat 'e' voorkomt in "dig", nul tijd. "e?" komt overeen met "set" omdat 'e' eenmalig voorkomt in "set",. Opmerking: “e?” komt nog steeds overeen met “schapen”; hoewel er twee 'e's in "schapen" zijn. Er is hier een nuance - zie later.

n,

Dit komt overeen met ten minste n opeenvolgende herhalingen van een voorafgaand teken of voorgaande groep. Dus de regex, "e2," komt overeen met de twee 'e's in het doel, "schapen", en de drie 'e's in het doel "schapen". "e2," komt niet overeen met "set", omdat "set" maar één 'e' heeft.

n

Dit komt overeen met precies n opeenvolgende herhalingen van een vorig teken of voorgaande groep. Dus de regex, "e2" komt overeen met de twee 'e's in het doel, "schapen". 'e2' komt niet overeen met 'set' omdat 'set' maar één 'e' heeft. Nou, "e2" komt overeen met twee 'e's in het doel, "schapen". Er is hier een nuance - zie later.

n,m

Dit komt overeen met verschillende opeenvolgende herhalingen van een voorafgaand teken of voorgaande groep, overal van n tot m, inclusief. Dus "e1,3" komt met niets overeen in "dig", dat geen 'e' heeft. Het komt overeen met de ene 'e' in 'set', de twee 'e's in 'sheep', de drie 'e's in 'sheeep' en drie 'e's in 'sheeep'. Er is een nuance bij de laatste wedstrijd - zie later.

Bijpassende afwisseling

Overweeg de volgende doelreeks in de computer:.

“De boerderij heeft varkens van verschillende groottes.”

De programmeur wil misschien weten of dit doelwit "geit" of "konijn" of "varken" heeft. De code zou als volgt zijn:

char str[] = "De boerderij heeft varkens van verschillende groottes.";
if (regex_search(str, regex("geit|konijn|varken")))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

De code produceert een match. Let op het gebruik van het afwisselingsteken, |. Er kunnen twee, drie, vier en meer opties zijn. C++ zal eerst proberen het eerste alternatief, "geit", te matchen op elke tekenpositie in de doelreeks. Als het niet lukt met "geit", probeert het het volgende alternatief, "konijn". Als het niet lukt met "konijn", probeert het het volgende alternatief, "varken". Als "varken" faalt, gaat C++ verder naar de volgende positie in het doel en begint opnieuw met het eerste alternatief.

In de bovenstaande code komt "varken" overeen.

Overeenkomend begin of einde

Begin


Als ^ aan het begin van de regex staat, kan de begintekst van de doelstring worden vergeleken met de regex. In de volgende code is het begin van het doel "abc", wat overeenkomt:

if (regex_search("abc en def", regex("^abc")))
cout << "matched" << endl;

Er vindt geen matching plaats in de volgende code:

if (regex_search("Ja, abc en def", regex("^abc")))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

Hier staat "abc" niet aan het begin van het doel.

Opmerking: het circumflex-teken, '^', is een metateken aan het begin van de regex, overeenkomend met het begin van de doelreeks. Het is nog steeds een metakarakter aan het begin van de karakterklasse, waar het de klasse negeert.

Einde

Als $ aan het einde van de regex staat, kan de eindtekst van de doelreeks worden vergeleken met de regex. In de volgende code is het einde van het doel "xyz", wat overeenkomt:

if (regex_search("uvw en xyz", regex("xyz$")))
cout << "matched" << endl;

Er vindt geen matching plaats in de volgende code:

if (regex_search("uvw en xyz final", regex("xyz$")))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

Hier staat "xyz" niet aan het einde van het doel.

Groepering

Haakjes kunnen worden gebruikt om tekens in een patroon te groeperen. Overweeg de volgende regex:

"een concert (pianist)"

De groep hier is "pianist" omringd door de metatekens ( en ). Het is eigenlijk een subgroep, terwijl "een concert (pianist)" de hele groep is. Stel je de volgende situatie voor:

"De (pianist is goed)"

Hier is de subgroep of substring: "pianist is goed".

Substrings met Common Parts

Een boekhouder is iemand die voor boeken zorgt. Stel je een bibliotheek voor met een boekhouder en boekenplank. Neem aan dat een van de volgende doelreeksen zich in de computer bevindt:

"De bibliotheek heeft een boekenplank die wordt bewonderd.";
"Hier is de boekhouder".";
"De boekhouder werkt met de boekenplank.";

Neem aan dat de programmeur er niet in geïnteresseerd is om te weten welke van deze zinnen in de computer staat. Toch is zijn interesse om te weten of "boekenplank" of "boekhouder" aanwezig is in de doelstring op de computer. In dit geval kan zijn regex zijn:

"boekenplank|boekhouder."

Afwisseling gebruiken.

Merk op dat "boek", dat beide woorden gemeen hebben, twee keer is getypt, in de twee woorden in het patroon. Om te voorkomen dat je "book" twee keer typt, kan de regex beter worden geschreven als:

"boek(plank|houder)"

Hier, de groep, "shelf|keeper" De afwisseling metakarakter is nog steeds gebruikt, maar niet voor twee lange woorden. Het is gebruikt voor de twee einddelen van de twee lange woorden. C++ behandelt een groep als een entiteit. Dus, C++ zoekt naar "plank" of "bewaarder" die direct na "boek" komt. De uitvoer van de volgende code is "matched":

char str[] = "De bibliotheek heeft een boekenplank die bewonderd wordt.";
if (regex_search(str, regex("book(shelf|keeper)")))
cout << "matched" << endl;

"boekenplank" en niet "boekhouder" zijn gevonden.

De icase en multiline regex_constants

icase

Matching is standaard hoofdlettergevoelig. Het kan echter hoofdletterongevoelig worden gemaakt. Gebruik hiervoor de regex::icase-constante, zoals in de volgende code:

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

De uitvoer is "gematcht". Dus "Feedback" met hoofdletter 'F' is gekoppeld aan "feed" met kleine letter 'f'. “regex::icase” is het tweede argument van de regex() constructor. Zonder dat zou de verklaring geen overeenkomst opleveren.

Multilijn

Beschouw de volgende code:

char str[] = "regel 1\nlijn 2\nlijn 3";
if (regex_search(str, regex("^.*$")))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

De uitvoer is "niet overeenkomend". De regex, "^.*$," komt overeen met de doelreeks van het begin tot het einde. “.*” betekent elk teken behalve \n, nul of meer keer. Dus vanwege de nieuwe regeltekens (\n) in het doel, was er geen overeenkomst.

Het doel is een string met meerdere regelsline. Om voor '.' om overeen te komen met het newline-teken, moet de constante "regex::multiline" worden gemaakt, het tweede argument van de regex()-constructie. De volgende code illustreert dit:

char str[] = "regel 1\nlijn 2\nlijn 3";
if (regex_search(str, regex("^.*$", regex::multiline)))
cout << "matched" << endl;
anders
cout << "not matched" << endl;

Overeenkomen met de hele doelreeks

Om de hele doelstring te matchen, die niet het newline-teken (\n) heeft, kan de functie regex_match() worden gebruikt. Deze functie verschilt van regex_search(). De volgende code illustreert dit:

char str[] = "eerste tweede derde";
if (regex_match(str, regex(".*tweede.*")))
cout << "matched" << endl;

Er is een match hier. Houd er echter rekening mee dat de regex overeenkomt met de hele doelreeks en dat de doelreeks geen '\n' heeft.

Het match_results-object

De functie regex_search() kan een argument opnemen tussen het doel en het regex-object. Dit argument is het object match_results. De hele overeenkomende (deel)tekenreeks en de gekoppelde subtekenreeksen kunnen ermee bekend zijn. Dit object is een speciale array met methoden. Het objecttype match_results is cmatch (voor letterlijke tekenreeksen).

Matches verkrijgen

Beschouw de volgende code:

char str[] = "De vrouw die je zocht!";
match m;
if (regex_search(str, m, regex("w.m.n")))
cout << m[0] << endl;

De doelstring heeft het woord "vrouw". De uitvoer is 'vrouw', wat overeenkomt met de regex, 'w.m.nt”. Bij index nul bevat de speciale array de enige overeenkomst, namelijk 'vrouw'.

Met klasse-opties wordt alleen de eerste subtekenreeks die in het doel wordt gevonden, naar de speciale array verzonden. De volgende code illustreert dit:

match m;
if (regex_search("De rat, de kat, de vleermuis!", m, regex("[bcr]at")))
cout << m[0] << endl;
cout << m[1] << endl;
cout << m[2] << endl;

De uitvoer is "rat" vanaf index nul. m[1] en m[2] zijn leeg.

Bij alternatieven wordt alleen de eerste substring die in het doel wordt gevonden, naar de speciale array gestuurd. De volgende code illustreert dit:

if (regex_search("Het konijn, de geit, het varken!", m, regex("geit|konijn|varken")))
cout << m[0] << endl;
cout << m[1] << endl;
cout << m[2] << endl;

De uitvoer is "konijn" vanaf index nul. m[1] en m[2] zijn leeg.

Groeperingen

Wanneer er groepen bij betrokken zijn, gaat het volledige patroon dat overeenkomt, naar cel nul van de speciale array. De volgende gevonden substring gaat naar cel 1; de substring die volgt, gaat naar cel 2; enzovoorts. De volgende code illustreert dit:

if (regex_search("Beste boekverkoper vandaag!", m, regex("book((sel)(ler))")))
cout << m[0] << endl;
cout << m[1] << endl;
cout << m[2] << endl;
cout << m[3] << endl;

De uitvoer is:

boekhandelaar
verkoper
zelf
ler

Merk op dat de groep (verkoper) vóór de groep (sel) komt.

Positie van wedstrijd

De positie van match voor elke substring in de cmatch-array kan bekend zijn. Het tellen begint vanaf het eerste teken van de doelreeks, op positie nul. De volgende code illustreert dit:

match m;
if (regex_search("Beste boekverkoper vandaag!", m, regex("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;

Let op het gebruik van de eigenschap position, met de celindex, als argument. De uitvoer is:

boekhandelaar->5
verkoper->9
sel->9
ler->12

Zoeken en vervangen

Een nieuw woord of nieuwe zin kan de overeenkomst vervangen. Hiervoor wordt de functie regex_replace() gebruikt. Deze keer is de tekenreeks waar de vervanging plaatsvindt echter het tekenreeksobject, niet de letterlijke tekenreeks. De stringbibliotheek moet dus in het programma worden opgenomen. Illustratie:

#include
#include
#include
namespace std; gebruiken;
int hoofd()

string str = "Hier, komt mijn man. Daar gaat je man.";
string newStr = regex_replace(str, regex("man"), "vrouw");
cout << newStr << endl;
retourneer 0;

De functie regex_replace(), zoals hier gecodeerd, vervangt alle overeenkomsten. Het eerste argument van de functie is het doel, het tweede is het regex-object en het derde is de vervangende string. De functie retourneert een nieuwe tekenreeks, die het doel is maar de vervanging heeft. De uitvoer is:

"Hier komt mijn vrouw". Daar gaat je vrouw.”

Conclusie

De reguliere expressie gebruikt patronen om subtekenreeksen in de doelreekstekenreeks te matchen. Patronen hebben metakarakters. Veelgebruikte functies voor C++ reguliere expressies zijn: regex_search(), regex_match() en regex_replace(). Een regex is een patroon tussen dubbele aanhalingstekens. Deze functies nemen echter het regex-object als argument en niet alleen de regex. Van de regex moet een regex-object worden gemaakt voordat deze functies het kunnen gebruiken.

Top Oculus App Lab-games
Als je eigenaar bent van een Oculus-headset, moet je op de hoogte zijn van sideloading. Sideloading is het proces van het installeren van niet-winkeli...
Top 10 spellen om te spelen op Ubuntu
Het Windows-platform is een van de dominante platforms voor gaming geweest vanwege het enorme percentage games dat tegenwoordig wordt ontwikkeld om Wi...
5 beste arcade-spellen voor Linux
Tegenwoordig zijn computers serieuze machines die worden gebruikt om te gamen. Als je de nieuwe hoge score niet kunt halen, weet je wat ik bedoel. In ...