Szukaj…


Wprowadzenie

Wyrażenia regularne (czasem nazywane wyrażeniami regularnymi lub wyrażeniami regularnymi) to składnia tekstowa, która reprezentuje wzorce, które można dopasować w obsługiwanych ciągach.

Wyrażenia regularne, wprowadzone w c ++ 11 , mogą opcjonalnie obsługiwać tablicę zwrotną dopasowanych ciągów lub inną składnię tekstową określającą sposób zastępowania dopasowanych wzorców w operowanych ciągach.

Składnia

  • regex_match // Zwraca, czy regex dopasował całą sekwencję znaków, opcjonalnie przechwytując w obiekcie dopasowania
  • regex_search // Zwraca czy część sekwencji znaków była dopasowana przez regex, opcjonalnie przechwytując w obiekcie dopasowania
  • regex_replace // Zwraca wejściową sekwencję znaków zmodyfikowaną przez regex za pomocą łańcucha formatu zastępczego
  • regex_token_iterator // Zainicjowany sekwencją znaków zdefiniowaną przez iteratory, listą indeksów przechwytywania do iteracji i wyrażeniem regularnym. Dereferencing zwraca aktualnie zindeksowane dopasowanie wyrażenia regularnego. Inkrementacja przesuwa się do następnego indeksu przechwytywania lub, jeśli aktualnie znajduje się na ostatnim indeksie, resetuje indeks i utrudnia następne wystąpienie dopasowania wyrażenia regularnego w sekwencji znaków
  • regex_iterator // Zainicjowany sekwencją znaków zdefiniowaną przez iteratory i wyrażenie regularne. Dereferencing zwraca część sekwencji znaków, którą aktualnie dopasowuje cały regex. Zwiększanie znajduje kolejne wystąpienie dopasowania wyrażenia regularnego w sekwencji znaków

Parametry

Podpis Opis
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) BidirectionalIterator to dowolny iterator znaków, który zapewnia operatorom inkrementacji i dekrementacji smatch może być cmatch lub dowolnym innym wariantem match_results który akceptuje typ BidirectionalIterator argument smatch może zostać pominięty, jeśli wyniki wyrażenia regularnego nie są potrzebne Zwraca, czy re pasuje do całego znaku sekwencja zdefiniowana przez first i last
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) string może być albo const char* lub L Wartość string funkcje przyjmującą wartość R string wyraźnie usunięte smatch może cmatch lub dowolny inny drugi wariant match_results przyjmująca typ str smatch argumentem może być pominięta, jeżeli wyniki wyrażenia regularnego nie są potrzebne Zwraca, czy re pasuje do całej sekwencji znaków zdefiniowanej przez str

Podstawowe przykłady wyrażeń regularnych i wyszukiwania wyrażeń regularnych

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"
    }
}

Przykład na żywo

regex_replace Przykład

Ten kod przyjmuje różne style nawiasów klamrowych i przekształca je w 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;

Przykład na żywo

regex_token_iterator Przykład

std::regex_token_iterator zapewnia ogromne narzędzie do wyodrębniania elementów pliku wartości oddzielonych przecinkami . Oprócz zalet iteracji, iterator może także przechwytywać przecinki, w których występują inne metody:

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"));

Przykład na żywo

Godnym uwagi problemem z iteratorami wyrażeń regularnych jest to, że argument regex musi być wartością L. Wartość R nie będzie działać .

regex_iterator Przykład

Gdy przetwarzanie przechwyceń musi być wykonywane iteracyjnie, regex_iterator jest dobrym wyborem. Dereferencja regex_iterator zwraca wynik match_result . Jest to świetne w przypadku przechwytywania warunkowego lub przechwytywania, które są współzależne. Powiedzmy, że chcemy tokenizować część kodu C ++. Dany:

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

Możemy tokenizować ten ciąg: const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s za pomocą regex_iterator takiego jak to:

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);
}

Przykład na żywo

Godnym uwagi problemem z iteratorami wyrażeń regularnych jest to, że argument regex musi być wartością L, wartość R nie będzie działać: Visual Studio regex_iterator Błąd?

Dzielenie sznurka

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"

Kwantyfikatory

Powiedzmy, że otrzymaliśmy const string input jako numer telefonu do sprawdzenia. Możemy zacząć od wprowadzenia danych liczbowych z zerowym lub większym kwantyfikatorem : regex_match(input, regex("\\d*")) lub jednym lub więcej kwantyfikatorem : regex_match(input, regex("\\d+")) Ale oba z nich naprawdę nie są wystarczające, jeśli input zawierają nieprawidłowy ciąg liczbowy, taki jak: „123” Użyjmy n lub więcej kwantyfikatora, aby upewnić się, że otrzymujemy co najmniej 7 cyfr:

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

To zagwarantuje, że otrzymamy co najmniej numer telefonu, ale input mogą również zawierać ciąg liczbowy, który jest zbyt długi, na przykład: „123456789012”. Przejdźmy więc do kwantyfikatora między n i m, aby dane input co najmniej 7 cyfr, ale nie więcej niż 11:

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

To nas zbliża, ale niedozwolone ciągi liczbowe z zakresu [7, 11] są nadal akceptowane, na przykład: „123456789” Ustawmy opcjonalny kod kraju za pomocą leniwego kwantyfikatora :

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

Ważne jest, aby pamiętać, że leniwy kwantyfikator dopasowuje jak najmniej znaków , więc jedynym sposobem dopasowania tego znaku jest, jeśli istnieje już 10 znaków, które zostały dopasowane przez \d{7,10} . (Aby zachłannie dopasować pierwszy znak, musielibyśmy zrobić: \d{0,1} .) Leniwy kwantyfikator można dołączyć do dowolnego innego kwantyfikatora.

Jak sprawić, by numer kierunkowy był opcjonalny i akceptować kod kraju tylko wtedy, gdy numer kierunkowy był obecny?

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

W tym ostatnim wyrażeniu \d{7} wymaga 7 cyfr. Te 7 cyfr jest opcjonalnie poprzedzone 3 lub 4 cyframi.

Zauważ, że nie dodaliśmy leniwego kwantyfikatora : \d{3,4}?\d{7} , \d{3,4}? dopasowałby 3 lub 4 znaki, preferując 3. Zamiast tego dopasowujemy grupę nie przechwytującą co najwyżej raz, woląc nie pasować. Powoduje niezgodność, jeśli input nie zawierają kodu obszaru, takiego jak: „1234567”.


Podsumowując temat kwantyfikatora, chciałbym wspomnieć o innym dołączanym kwantyfikatorze, którego można użyć, kwantyfikat dzierżawczy . Zarówno leniwy kwantyfikator, jak i kwantyfikator dzierżawczy można dołączyć do dowolnego kwantyfikatora. Jedyną funkcją kwantyfikatora dzierżawczego jest wspomaganie silnika wyrażeń regularnych poprzez mówienie, łapczywie zabierać te znaki i nigdy ich nie poddawać, nawet jeśli powoduje to, że wyrażenie regularne kończy się niepowodzeniem . To na przykład nie ma większego sensu: regex_match(input, regex("\\d{3,4}+\\d{7})) Ponieważ input takie jak:„ 1234567890 ”nie byłyby dopasowane jako \d{3,4}+ zawsze będzie pasować do 4 znaków, nawet jeśli dopasowanie 3 pozwoliłoby na wyrażenie regularne.
Kwantyfikator dzierżawczy najlepiej stosować, gdy skwantyfikowany token ogranicza liczbę pasujących znaków . Na przykład:

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

Można użyć do dopasowania, jeśli input zawierały jedno z poniższych:

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

Ale kiedy ten regex naprawdę świeci, to gdy input zawierają nieprawidłowe dane wejściowe:

12345–67890

Bez kwantyfikatora dzierżawczego silnik regex musi cofnąć się i przetestować każdą kombinację .* Oraz 3 lub 4 znaki, aby sprawdzić, czy może znaleźć pasującą kombinację. Z zaborczej kwantyfikatorem rozpoczyna regex gdzie 2 nd zaborczy kwantyfikator przerwano, znak „0”, i próbuje silnik regex aby dostosować .* , Aby umożliwić \d{3,4} , aby dopasować; gdy nie może regex po prostu zawodzi, nie jest wykonywane śledzenie wsteczne, aby sprawdzić, czy wcześniej .* dostosowanie mogło pozwolić na dopasowanie.

Kotwice

C ++ zapewnia tylko 4 kotwice:

  • ^ który zapewnia początek łańcucha
  • $ który zapewnia koniec łańcucha
  • \b który zapewnia znak \W lub początek lub koniec ciągu
  • \B który zapewnia znak \w

Powiedzmy na przykład, że chcemy uchwycić liczbę ze znakiem:

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+)" }));
}

Przykład na żywo

Ważną uwagą jest to, że kotwica nie zużywa żadnych znaków.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow