Zoeken…


Invoering

Reguliere expressies (soms regexs of regexps genoemd) zijn een tekstuele syntaxis die de patronen weergeeft die kunnen worden vergeleken in de bewerkte tekenreeksen.

Reguliere expressies, geïntroduceerd in c ++ 11 , kunnen optioneel een retourreeks van overeenkomende tekenreeksen of een andere tekstuele syntaxis ondersteunen die definieert hoe overeenkomende patronen te vervangen in bewerkte tekenreeksen.

Syntaxis

  • regex_match // Retourneert of de volledige tekenreeks is gematcht door de regex, eventueel vastgelegd in een matchobject
  • regex_search // Retourneert of een deel van de tekenreeks is gematcht door de regex, eventueel vastgelegd in een matchobject
  • regex_replace // Retourneert de ingevoerde tekenreeks zoals gewijzigd door een regex via een vervangende opmaakreeks
  • regex_token_iterator // Geïnitialiseerd met een tekenreeks gedefinieerd door iterators, een lijst met vast te leggen indexen en een regex. Dereferencing retourneert de momenteel geïndexeerde overeenkomst van de regex. Toenemende verhuizingen naar de volgende opname-index of, indien momenteel op de laatste index, de index opnieuw instellen en het volgende optreden van een regex-overeenkomst in de tekenreeks achterhalen
  • regex_iterator // Geïnitialiseerd met een tekenreeks gedefinieerd door iterators en een regex. Dereferencing retourneert het gedeelte van de tekenreeks waarin de hele regex momenteel overeenkomt. Verhogen vindt het volgende optreden van een regex-overeenkomst in de tekenreeks

parameters

Handtekening Beschrijving
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) BidirectionalIterator is elke teken-iterator die voorziet in increment en decrement operators. smatch kan cmatch of elke andere variant van match_results die het type BidirectionalIterator accepteert. Het smatch argument kan worden weggelaten als de resultaten van de regex niet nodig zijn. Retourneert of re overeenkomt met het hele teken volgorde gedefinieerd door first en last
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) string kan een const char* of een L-Value string , de functies die een R-Value string accepteren zijn expliciet verwijderd smatch kunnen cmatch of een andere variant van match_results die het type str accepteert, het smatch argument kan worden weggelaten als de resultaten van de regex zijn niet nodig Retourneert of re overeenkomt met de volledige tekenreeks die wordt gedefinieerd door str

Basic regex_match en regex_search voorbeelden

const auto input = "Some people, when confronted with a problem, think \"I know, I'll use regular expressions.\""s;
smatch sm;

cout << input << endl;

// If input ends in a quotation that contains a word that begins with "reg" and another word begining with "ex" then capture the preceeding portion of input
if (regex_match(input, sm, regex("(.*)\".*\\breg.*\\bex.*\"\\s*$"))) {
    const auto capture = sm[1].str();
    
    cout << '\t' << capture << endl; // Outputs: "\tSome people, when confronted with a problem, think\n"
    
    // Search our capture for "a problem" or "# problems"
    if(regex_search(capture, sm, regex("(a|d+)\\s+problems?"))) {
        const auto count = sm[1] == "a"s ? 1 : stoi(sm[1]);
        
        cout << '\t' << count << (count > 1 ? " problems\n" : " problem\n"); // Outputs: "\t1 problem\n"
        cout << "Now they have " << count + 1 << " problems.\n"; // Ouputs: "Now they have 2 problems\n"
    }
}

Live voorbeeld

regex_replace Voorbeeld

Deze code bevat verschillende brace-stijlen en converteert deze naar One True Brace Style :

const auto input = "if (KnR)\n\tfoo();\nif (spaces) {\n    foo();\n}\nif (allman)\n{\n\tfoo();\n}\nif (horstmann)\n{\tfoo();\n}\nif (pico)\n{\tfoo(); }\nif (whitesmiths)\n\t{\n\tfoo();\n\t}\n"s;

cout << input << regex_replace(input, regex("(.+?)\\s*\\{?\\s*(.+?;)\\s*\\}?\\s*"), "$1 {\n\t$2\n}\n") << endl;

Live voorbeeld

regex_token_iterator Voorbeeld

Een std::regex_token_iterator biedt een geweldig hulpmiddel voor het extraheren van elementen van een Comma Separated Value-bestand . Afgezien van de voordelen van iteratie, kan deze iterator ook ontsnapte komma's vastleggen waar andere methoden moeite mee hebben:

const auto input = "please split,this,csv, ,line,\\,\n"s;
const regex re{ "((?:[^\\\\,]|\\\\.)+)(?:,|$)" };
const vector<string> m_vecFields{ sregex_token_iterator(cbegin(input), cend(input), re, 1), sregex_token_iterator() };

cout << input << endl;

copy(cbegin(m_vecFields), cend(m_vecFields), ostream_iterator<string>(cout, "\n"));

Live voorbeeld

Een opmerkelijke gotcha met regex-iterators is dat het regex argument een L-waarde moet zijn. Een R-waarde werkt niet .

regex_iterator Voorbeeld

Wanneer het verwerken van opnames iteratief moet gebeuren, is een regex_iterator een goede keuze. regex_iterator een regex_iterator levert een match_result . Dit is geweldig voor voorwaardelijke vastleggingen of vastleggingen die onderling afhankelijk zijn. Laten we zeggen dat we wat C ++ -code willen tokeniseren. Gegeven:

enum TOKENS {
    NUMBER,
    ADDITION,
    SUBTRACTION,
    MULTIPLICATION,
    DIVISION,
    EQUALITY,
    OPEN_PARENTHESIS,
    CLOSE_PARENTHESIS
};

We kunnen deze string tokeniseren: const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s met een regex_iterator als deze:

vector<TOKENS> tokens;
const regex re{ "\\s*(\\(?)\\s*(-?\\s*\\d+)\\s*(\\)?)\\s*(?:(\\+)|(-)|(\\*)|(/)|(=))" };

for_each(sregex_iterator(cbegin(input), cend(input), re), sregex_iterator(), [&](const auto& i) {
    if(i[1].length() > 0) {
        tokens.push_back(OPEN_PARENTHESIS);
    }
    
    tokens.push_back(i[2].str().front() == '-' ? NEGATIVE_NUMBER : NON_NEGATIVE_NUMBER);
    
    if(i[3].length() > 0) {
        tokens.push_back(CLOSE_PARENTHESIS);
    }        
    
    auto it = next(cbegin(i), 4);
    
    for(int result = ADDITION; it != cend(i); ++result, ++it) {
        if (it->length() > 0U) {
            tokens.push_back(static_cast<TOKENS>(result));
            break;
        }
    }
});

match_results<string::const_reverse_iterator> sm;

if(regex_search(crbegin(input), crend(input), sm, regex{ tokens.back() == SUBTRACTION ? "^\\s*\\d+\\s*-\\s*(-?)" : "^\\s*\\d+\\s*(-?)" })) {
    tokens.push_back(sm[1].length() == 0 ? NON_NEGATIVE_NUMBER : NEGATIVE_NUMBER);
}

Live voorbeeld

Een opvallende gotcha met regex iterators is dat het regex een L-waarde moet zijn, een R-waarde werkt niet: Visual Studio regex_iterator Bug?

Een string splitsen

std::vector<std::string> split(const std::string &str, std::string regex)
{
    std::regex r{ regex };
    std::sregex_token_iterator start{ str.begin(), str.end(), r, -1 }, end;
    return std::vector<std::string>(start, end);
}
split("Some  string\t with whitespace ", "\\s+"); // "Some", "string", "with", "whitespace"

quantifiers

Laten we zeggen dat we const string input als een te valideren telefoonnummer. We kunnen beginnen met het vereisen van een numerieke invoer met een nul of meer kwantificeerder : regex_match(input, regex("\\d*")) of een een of meer kwantificeerder : regex_match(input, regex("\\d+")) Maar beide komen echt tekort als input een ongeldige numerieke reeks bevat zoals: "123" Laten we een n of meer kwantificator gebruiken om ervoor te zorgen dat we ten minste 7 cijfers krijgen:

regex_match(input, regex("\\d{7,}"))

Dit garandeert dat we ten minste een telefoonnummer krijgen, maar input kan ook een numerieke tekenreeks bevatten die te lang is zoals: "123456789012". Dus laten we gaan met een tussen n en m kwantificeerder zodat de input minimaal 7 cijfers maar niet meer dan 11 cijfers heeft:

regex_match(input, regex("\\d{7,11}"));

Dit brengt ons dichterbij, maar illegale numerieke tekenreeksen in het bereik van [7, 11] worden nog steeds geaccepteerd, zoals: "123456789" Dus laten we de landcode optioneel maken met een luie kwantificator :

regex_match(input, regex("\\d?\\d{7,10}"))

Het is belangrijk op te merken dat de luie kwantificator zo weinig mogelijk tekens overeenkomt , dus de enige manier waarop dit teken wordt gekoppeld, is als er al 10 tekens zijn die overeenkomen met \d{7,10} . (Om gretig met het eerste karakter overeen te komen, hadden we moeten doen: \d{0,1} .) De luie kwantificeerder kan aan elke andere kwantificeerder worden toegevoegd.

Hoe zouden we het netnummer optioneel maken en alleen een landcode accepteren als het netnummer aanwezig was?

regex_match(input, regex("(?:\\d{3,4})?\\d{7}"))

In deze laatste regex vereist de \d{7} 7 cijfers. Deze 7 cijfers worden optioneel voorafgegaan door 3 of 4 cijfers.

Merk op dat we de luie kwantificator niet hebben toegevoegd: \d{3,4}?\d{7} , de \d{3,4}? zou 3 of 4 tekens hebben gematcht, bij voorkeur 3. In plaats daarvan maken we de niet-vastleggende groepsmatch maximaal één keer, liever niet. Het veroorzaken van een mismatch als input niet het netnummer bevatte zoals: "1234567".


Ter afsluiting van het kwantificeringsonderwerp wil ik de andere toegevoegde kwantificator vermelden die u kunt gebruiken, de bezittelijke kwantificator . Of de luie kwantificeerder of de bezittelijke kwantificeerder kan aan elke kwantificeerder worden toegevoegd. De enige functie van de bezitterige kwantificator is om de regex-engine te helpen door het te vertellen, deze karakters gretig te nemen en ze nooit op te geven, zelfs als het ervoor zorgt dat de regex mislukt . Dit heeft bijvoorbeeld weinig zin: regex_match(input, regex("\\d{3,4}+\\d{7})) Omdat een input als:" 1234567890 "niet zou worden gekoppeld als \d{3,4}+ komt altijd overeen met 4 tekens, zelfs als met matching 3 de regex zou hebben kunnen slagen.
De bezittelijke kwantificator wordt het best gebruikt wanneer het gekwantificeerde token het aantal overeenkomende tekens beperkt . Bijvoorbeeld:

regex_match(input, regex("(?:.*\\d{3,4}+){3}"))

Kan worden gebruikt om te matchen als input een van de volgende input bevat:

123 456 7890
123-456-7890
(123)456-7890
(123) 456 - 7890

Maar wanneer deze regex echt schijnt, is wanneer input een illegale invoer bevat:

12345 - 67890

Zonder de bezitterige kwantificator moet de regex-engine teruggaan en elke combinatie van .* En 3 of 4 tekens testen om te zien of hij een overeenkomende combinatie kan vinden. Met de bezittelijke kwantificator begint de regex waar de 2e bezitterige kwantificator is gestopt, het '0'-teken, en de regex-engine probeert de .* Aan te passen om \d{3,4} overeen te laten komen; wanneer dit niet lukt, mislukt de regex gewoon, er wordt geen back-tracking uitgevoerd om te zien of eerder .* aanpassing een match had kunnen toestaan.

ankers

C ++ biedt slechts 4 ankers:

  • ^ die het begin van de string bevestigt
  • $ die het einde van de string bevestigt
  • \b waarin een \W teken of het begin of einde van de tekenreeks wordt geplaatst
  • \B die een \w karakter opgeeft

Laten we zeggen dat we bijvoorbeeld een nummer met het teken willen vastleggen:

auto input = "+1--12*123/+1234"s;
smatch sm;

if(regex_search(input, sm, regex{ "(?:^|\\b\\W)([+-]?\\d+)" })) {

    do {
        cout << sm[1] << endl;
        input = sm.suffix().str();
    } while(regex_search(input, sm, regex{ "(?:^\\W|\\b\\W)([+-]?\\d+)" }));
}

Live voorbeeld

Een belangrijke opmerking hierbij is dat het anker geen tekens verbruikt.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow