C++
Espressioni regolari
Ricerca…
introduzione
Le espressioni regolari (a volte chiamate regex o espressioni regolari) sono una sintassi testuale che rappresenta i modelli che possono essere confrontati nelle stringhe operate.
Le espressioni regolari, introdotte in C ++ 11 , possono opzionalmente supportare un array di restituzione di stringhe corrispondenti o un'altra sintassi testuale che definisce come sostituire schemi abbinati nelle stringhe su cui si opera.
Sintassi
- regex_match // Restituisce se l'intera sequenza di caratteri è stata abbinata dalla regex, eventualmente acquisendo in un oggetto match
- regex_search // Indica se una parte della sequenza di caratteri è stata abbinata alla regex, eventualmente acquisendo in un oggetto match
- regex_replace // Restituisce la sequenza di caratteri di input modificata da un'espressione regolare tramite una stringa di formato di sostituzione
- regex_token_iterator // Inizializzato con una sequenza di caratteri definita da iteratori, una lista di indici di cattura da iterare e una regex. Dereferencing restituisce la corrispondenza indicizzata al momento della regex. Incrementando le mosse al successivo indice di cattura o se attualmente nell'ultimo indice, reimposta l'indice e incide l'occorrenza successiva di una corrispondenza regolare nell'esecuzione dei caratteri
- regex_iterator // Inizializzato con una sequenza di caratteri definita da iteratori e una regex. Il dereferenziamento restituisce la parte della sequenza di caratteri che corrisponde attualmente all'intera espressione regolare. Incrementando trova la prossima occorrenza di una corrispondenza regolare nell'esecuzione dei caratteri
Parametri
Firma | Descrizione |
---|---|
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) | BidirectionalIterator è qualsiasi iteratore di caratteri che fornisce operatori di incremento e decremento smatch può essere cmatch o qualsiasi altra variante di match_results che accetta il tipo di BidirectionalIterator l'argomento smatch può essere omesso se i risultati della regex non sono necessari. Restituisce se re combacia con l'intero carattere sequenza definita dal first e last |
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) | string può essere un const char* o una string L-Value, le funzioni che accettano una string R-Value sono eliminate esplicitamente smatch può essere cmatch o qualsiasi altra variante di match_results che accetta il tipo di str l'argomento smatch può essere omesso se i risultati del l'espressione regolare non sono necessari Indica se re corrisponde l'intera sequenza di caratteri definito da str |
Esempi di regex_match e regex_search di base
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 Esempio
Questo codice contiene vari stili di controvento e li converte in uno stile True True :
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 Esempio
Un std::regex_token_iterator
fornisce un formidabile strumento per estrarre elementi da un file di valori separati da virgola . Oltre ai vantaggi dell'iterazione, questo iteratore è anche in grado di catturare virgole sfuggite dove altri metodi combattono:
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"));
Un clamore notevole con gli iteratori di espressioni regolari è che l'argomento di regex
regolare deve essere un valore L. Un valore R non funzionerà .
regex_iterator Esempio
Quando l'elaborazione delle acquisizioni deve essere eseguita in modo iterativo, regex_iterator
è una buona scelta. regex_iterator
un regex_iterator
restituisce un match_result
. Questo è ottimo per le catture condizionali o catture che hanno interdipendenza. Diciamo che vogliamo tokenize del codice C ++. Dato:
enum TOKENS {
NUMBER,
ADDITION,
SUBTRACTION,
MULTIPLICATION,
DIVISION,
EQUALITY,
OPEN_PARENTHESIS,
CLOSE_PARENTHESIS
};
Possiamo tokenize questa stringa: const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s
con un regex_iterator
come questo:
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);
}
Un notcha notabile con gli iteratori regex è che l'argomento regex
regolare deve essere un valore L, un valore R non funzionerà: Visual Studio regex_iterator Bug?
Divisione di una corda
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"
quantificatori
Diciamo che ci viene data una const string input
come numero di telefono per essere convalidato. Potremmo iniziare richiedendo un input numerico con zero o più quantificatori : regex_match(input, regex("\\d*"))
o uno o più quantificatori : regex_match(input, regex("\\d+"))
Ma entrambi sono davvero in difetto se l' input
contiene una stringa numerica non valida come: "123" Usiamo un n o più quantificatori per assicurarci di ottenere almeno 7 cifre:
regex_match(input, regex("\\d{7,}"))
Ciò garantirà che otterremo almeno un numero di telefono di cifre, ma l' input
potrebbe contenere anche una stringa numerica troppo lunga come "123456789012". Quindi lascia andare con un quantificatore tra n e m in modo che l' input
sia di almeno 7 cifre ma non più di 11:
regex_match(input, regex("\\d{7,11}"));
Questo ci avvicina, ma le stringhe numeriche illegali che sono nell'intervallo [7, 11] sono ancora accettate, come: "123456789" Quindi rendiamo opzionale il codice paese con un quantificatore pigro :
regex_match(input, regex("\\d?\\d{7,10}"))
È importante notare che il quantificatore pigro corrisponde al minor numero possibile di caratteri , quindi l'unico modo in cui questo carattere verrà confrontato è se ci sono già 10 caratteri che sono stati abbinati da \d{7,10}
. (Per abbinare avidamente il primo personaggio avremmo dovuto fare: \d{0,1}
.) Il quantificatore pigro può essere aggiunto a qualsiasi altro quantificatore.
Ora, come potremmo rendere facoltativo il prefisso e accettare solo un prefisso internazionale se fosse presente il prefisso?
regex_match(input, regex("(?:\\d{3,4})?\\d{7}"))
In questa regex finale, il \d{7}
richiede 7 cifre. Queste 7 cifre sono opzionalmente precedute da 3 o 4 cifre.
Si noti che non abbiamo aggiunto il quantificatore pigro : , il \d{3,4}?\d{7}
\d{3,4}?
avrebbe dovuto corrispondere a 3 o 4 caratteri, preferendo 3. Invece faremo la partita del gruppo non catturante al massimo una volta, preferendo non corrispondere. Causando una mancata corrispondenza se l' input
non includeva il prefisso come: "1234567".
In conclusione dell'argomento quantificatore, vorrei menzionare l'altro quantificatore che puoi usare, il quantificatore possessivo . O il quantificatore pigro o quantificatore possessivo può essere aggiunto a qualsiasi quantificatore. L'unica funzione del quantificatore possessivo è quella di assistere il motore regex dicendolo, prendendo avidamente questi caratteri e non li abbandonare mai, anche se causa il regex fallire . Questo per esempio non ha molto senso: regex_match(input, regex("\\d{3,4}+\\d{7}))
Perché un input
come:" 1234567890 "non verrebbe abbinato come \d{3,4}+
corrisponderà sempre a 4 caratteri, anche se la corrispondenza 3 avrebbe permesso al regex di avere successo.
Il quantificatore possessivo viene utilizzato al meglio quando il token quantificato limita il numero di caratteri accoppiabili . Per esempio:
regex_match(input, regex("(?:.*\\d{3,4}+){3}"))
Può essere utilizzato per corrispondere se l' input
contiene uno dei seguenti elementi:
123 456 7890
123-456-7890
(123)456-7890
(123) 456 - 7890
Ma quando questa regex brilla davvero è quando l' input
contiene un input illegale :
12345 - 67890
Senza il quantificatore possessivo il motore regex deve tornare indietro e testare ogni combinazione di .*
e di 3 o 4 caratteri per vedere se può trovare una combinazione abbinabile. Con il quantificatore possessivo le regex inizia dove il quantificatore 2 ° possessivo lasciato, il carattere '0', e il motore di regex cerca di regolare il .*
Per consentire \d{3,4}
per abbinare; quando non può la regex appena fallisce, nessun monitoraggio posteriore è fatto per vedere se precedente .*
Regolazione avrebbe potuto consentire una corrispondenza.
ancore
C ++ fornisce solo 4 ancore:
-
^
che asserisce l'inizio della stringa -
$
che asserisce la fine della stringa -
\b
che asserisce un carattere\W
o l'inizio o la fine della stringa -
\B
che asserisce un carattere\w
Diciamo per esempio che vogliamo catturare un numero con il suo segno:
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+)" }));
}
Una nota importante qui è che l'ancora non consuma alcun personaggio.