C++
Reguläre Ausdrücke
Suche…
Einführung
Reguläre Ausdrücke (manchmal auch Regex oder Regex genannt) sind eine Textsyntax, die die Muster darstellt, die in den bearbeiteten Strings abgeglichen werden können.
Reguläre Ausdrücke, die in c ++ 11 eingeführt wurden , unterstützen optional ein Rückgabefeld aus übereinstimmenden Zeichenfolgen oder eine andere Textsyntax, die definiert, wie übereinstimmende Muster in Zeichenfolgen ersetzt werden, für die ein Vorgang ausgeführt wird.
Syntax
- regex_match // Gibt zurück, ob die gesamte Zeichenfolge vom regulären Ausdruck abgeglichen wurde, wobei optional ein Übereinstimmungsobjekt erfasst wird
- regex_search // Gibt zurück, ob ein Teil der Zeichenfolge vom regulären Ausdruck abgeglichen wurde, wobei optional ein Übereinstimmungsobjekt erfasst wird
- regex_replace // Gibt die Eingabezeichenfolge zurück, wie sie von einem regulären Ausdruck geändert wurde, und zwar über einen Ersetzungsformat-String
- regex_token_iterator // Initialisiert mit einer von Iteratoren definierten Zeichenfolge, einer Liste von Erfassungsindizes, die durchlaufen werden sollen, und einer Regex. Dereferencing gibt die aktuell indizierte Übereinstimmung der Regex zurück. Durch Inkrementieren zum nächsten Capture-Index oder wenn aktuell im letzten Index, wird der Index zurückgesetzt und das nächste Vorkommen einer Regex-Übereinstimmung in der Zeichenfolge verhindert
- regex_iterator // Mit einer durch Iteratoren definierten Zeichenfolge und Regex initialisiert. Dereferencing gibt den Teil der Zeichenfolge zurück, auf den der gesamte reguläre Ausdruck passt. Beim Inkrementieren wird das nächste Vorkommen einer Regex-Übereinstimmung in der Zeichenfolge gefunden
Parameter
Unterschrift | Beschreibung |
---|---|
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) | BidirectionalIterator ist jedes Zeichen Iterator, Zuwachs und Dekrementoperatoren bietet smatch sein kann cmatch oder jede andere andere Variante von match_results , die die Art der nimmt BidirectionalIterator das smatch Argument weggelassen werden, wenn die Ergebnisse der Regex nicht Returns benötigt , ob re den gesamten Zeichenspiele Reihenfolge definiert als first und last |
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) | string kann entweder eine sein const char* oder eine L-Wert - string , die Funktionen einen R-Wert zu übernehmen string explizit gelöscht smatch kann cmatch oder jede andere andere Variante von match_results , die die Art von übernimmt str das smatch Argument weggelassen werden , wenn Die Ergebnisse des regulären Ausdrucks werden nicht benötigt. Gibt zurück, ob re mit der gesamten durch str definierten Zeichenfolge übereinstimmt |
Grundlegende Beispiele für "regex_match" und "regex_search"
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"
}
}
regex_replace Beispiel
Dieser Code berücksichtigt verschiedene Klammerstile und konvertiert sie in einen echten Klammerstil :
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;
regex_token_iterator Beispiel
Ein std::regex_token_iterator
bietet ein enormes Werkzeug zum Extrahieren von Elementen einer Comma Separated Value-Datei . Abgesehen von den Vorteilen der Iteration kann dieser Iterator auch Kommas einfangen, wenn andere Methoden Probleme haben:
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"));
Ein bemerkenswertes Problem bei Regex-Iteratoren ist, dass das regex
Argument ein L-Wert sein muss. Ein R-Wert funktioniert nicht .
regex_iterator Beispiel
Wenn die Verarbeitung von Captures iterativ durchgeführt werden regex_iterator
ist ein regex_iterator
eine gute Wahl. Wenn ein regex_iterator
ein match_result
. Dies ist ideal für konditionale Captures oder Captures mit wechselseitiger Abhängigkeit. Nehmen wir an, wir wollen etwas C ++ - Code tokenisieren. Gegeben:
enum TOKENS {
NUMBER,
ADDITION,
SUBTRACTION,
MULTIPLICATION,
DIVISION,
EQUALITY,
OPEN_PARENTHESIS,
CLOSE_PARENTHESIS
};
Wir können diese Zeichenkette mit einem Token versehen: const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s
mit einem regex_iterator
wie regex_iterator
:
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);
}
Ein bemerkenswertes Argument bei Regex-Iteratoren ist, dass das regex
Argument ein L-Wert sein muss. Ein R-Wert funktioniert nicht: Visual Studio regex_iterator Fehler?
Einen String aufteilen
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"
Quantifizierer
Nehmen wir an, wir haben eine const string input
als zu überprüfende Telefonnummer. Wir könnten mit einer numerischen Eingabe mit einem Null- oder mehr-Quantifizierer beginnen : regex_match(input, regex("\\d*"))
oder einem oder mehreren Quantifizierer : regex_match(input, regex("\\d+"))
Beide sind wirklich unzureichend, wenn die input
eine ungültige numerische Zeichenfolge enthält: "123". Verwenden Sie einen Quantifizierer n oder mehr, um sicherzustellen, dass wir mindestens 7 Ziffern erhalten:
regex_match(input, regex("\\d{7,}"))
Dies garantiert, dass wir mindestens eine Telefonnummer mit Ziffern erhalten, aber die input
könnte auch eine numerische Zeichenfolge enthalten, die zu lang ist: "123456789012". Also mit einem Quantifizierer zwischen n und m, so dass die input
mindestens 7 Ziffern, aber nicht mehr als 11 hat:
regex_match(input, regex("\\d{7,11}"));
Dies bringt uns näher, aber illegale numerische Zeichenfolgen, die im Bereich von [7, 11] liegen, werden immer noch akzeptiert, wie: "123456789". Lassen Sie uns den Ländercode mit einem Lazy-Quantifizierer optional machen:
regex_match(input, regex("\\d?\\d{7,10}"))
Es ist wichtig zu wissen, dass der faule Quantifizierer so wenig Zeichen wie möglich enthält. Die Übereinstimmung des Zeichens ist daher nur möglich , wenn bereits 10 Zeichen mit \d{7,10}
übereinstimmen. (Um das erste Zeichen gierig anzupassen, müssten wir Folgendes tun: \d{0,1}
.) Der faule Quantifizierer kann an jeden anderen Quantifizierer angehängt werden.
Wie würden wir die Vorwahl optional machen und eine Landesvorwahl nur akzeptieren, wenn die Vorwahl vorhanden ist?
regex_match(input, regex("(?:\\d{3,4})?\\d{7}"))
In diesem letzten regulären Ausdruck, der \d{7}
erfordert 7 Stellen. Vor diesen 7 Ziffern stehen wahlweise 3 oder 4 Ziffern.
Beachten Sie, dass der faule Quantifizierer nicht angehängt wurde: , der \d{3,4}?\d{7}
\d{3,4}?
hätte entweder 3 oder 4 Zeichen gefunden und würde 3 bevorzugen. Stattdessen machen wir die Gruppe, die keine Capture erfasst, höchstens einmal. Ursache einer Nichtübereinstimmung, wenn die input
keine Vorwahl wie "1234567" enthielt.
Zum Abschluss des Quantifikator-Themas möchte ich den anderen anhängigen Quantifizierer erwähnen, den Sie verwenden können, den Possessiv-Quantifizierer . Entweder der Lazy Quantifier oder der Possessive Quantifier kann an einen beliebigen Quantifier angehängt werden. Die einzige Funktion des Possessiv-Quantifizierers besteht darin, die Regex-Engine zu unterstützen, indem sie sie dazu auffordert, diese Charaktere gierig zu nehmen und sie niemals aufzugeben, selbst wenn dadurch der Regex fehlschlägt . Dies macht zum Beispiel nicht viel Sinn: regex_match(input, regex("\\d{3,4}+\\d{7}))
Da eine input
wie:" 1234567890 "nicht als \d{3,4}+
übereinstimmen würde \d{3,4}+
stimmt immer mit 4 Zeichen überein, selbst wenn die Übereinstimmung mit 3 den Regex erfolgreich gemacht hätte.
Der Possessiv-Quantifizierer wird am besten verwendet, wenn das quantifizierte Token die Anzahl anpassbarer Zeichen begrenzt . Zum Beispiel:
regex_match(input, regex("(?:.*\\d{3,4}+){3}"))
Kann verwendet werden, wenn die input
eine der folgenden input
enthält:
123 456 7890
123-456-7890
(123)456-7890
(123) 456 - 7890
Wenn dieser Regex wirklich glänzt, dann ist die input
eine unzulässige Eingabe:
12345 - 67890
Ohne den Possessiv-Quantifizierer muss die Regex-Engine zurückgehen und jede Kombination von .*
Und entweder 3 oder 4 Zeichen testen, um zu sehen, ob sie eine übereinstimmende Kombination findet. Mit dem besitzergreifend quantifier wo die regulären Ausdruck beginnt die 2. besitzergreifend quantifier aufgehört hat , die ‚0‘, und der Motor versucht der regex einzustellen . .*
Ermöglichen \d{3,4}
zu entsprechen; wenn es nicht die Regex nur fehlschlägt, wird keine Rückverfolgung zu sehen getan , wenn früher .*
Einstellung ein Spiel erlaubt hätte.
Anker
C ++ bietet nur 4 Anker:
-
^
was den Start der Zeichenkette bestätigt -
$
die das Ende der Zeichenfolge bestätigt -
\b
das ein\W
Zeichen oder den Anfang oder das Ende der Zeichenfolge bestätigt -
\B
das ein\w
-Zeichen bestätigt
Nehmen wir zum Beispiel an, wir möchten eine Zahl mit ihrem Vorzeichen erfassen:
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+)" }));
}
Ein wichtiger Hinweis hierbei ist, dass der Anker keine Zeichen verbraucht.