Recherche…


Introduction

Les expressions régulières (parfois appelées regexs ou regexps) sont une syntaxe textuelle qui représente les modèles pouvant être mis en correspondance dans les chaînes utilisées.

Les expressions régulières, introduites dans c ++ 11 , peuvent éventuellement prendre en charge un tableau de retour de chaînes correspondantes ou une autre syntaxe textuelle définissant la manière de remplacer les modèles correspondants dans les chaînes sur lesquelles elles sont exécutées.

Syntaxe

  • regex_match // Indique si l'intégralité de la séquence de caractères a été appariée par l'expression régulière, éventuellement capturée dans un objet de correspondance
  • regex_search // Indique si une partie de la séquence de caractères a été appariée par l'expression régulière, éventuellement capturée dans un objet de correspondance
  • regex_replace // Renvoie la séquence de caractères d'entrée modifiée par une regex via une chaîne de format de remplacement
  • regex_token_iterator // Initialisé avec une séquence de caractères définie par les itérateurs, une liste des index de capture à parcourir et une expression régulière. Dereferencing renvoie la correspondance actuellement indexée du regex. L'incrémentation passe à l'index de capture suivant ou, si elle se trouve au dernier index, réinitialise l'index et empêche la prochaine occurrence d'une correspondance regex dans la séquence de caractères.
  • regex_iterator // Initialisé avec une séquence de caractères définie par des itérateurs et une regex. Le déréférencement renvoie la partie de la séquence de caractères à laquelle correspond la regex entière. L'incrémentation trouve l'occurrence suivante d'une correspondance d'expression régulière dans la séquence de caractères

Paramètres

Signature La description
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) BidirectionalIterator est tout iterator de caractères qui fournit incrémentation et les opérateurs décrémentation smatch peuvent être cmatch ou tout autre autre variante de match_results qui accepte le type de BidirectionalIterator le smatch argument peut être ignoré si les résultats de l'expression régulière ne sont pas nécessaires Vérifie si re correspond au caractère entier séquence définie par le first et le last
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) string peut être un const char* ou une string L-Value, les fonctions acceptant une string R-Value sont explicitement supprimées smatch peut être cmatch ou toute autre variante de match_results qui accepte le type de str l'argument smatch peut être omis si les résultats du regex ne sont pas nécessaires Renvoie si re correspond à toute la séquence de caractères définie par str
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"
    }
}

Exemple Live

Exemple de regex_replace

Ce code prend différents styles d'accolades et les convertit en un seul style d'accolade:

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;

Exemple Live

Exemple avec regex_token_iterator

Un std::regex_token_iterator fournit un formidable outil pour extraire des éléments d'un fichier de valeurs séparées par des virgules . Outre les avantages de l’itération, cet itérateur est également capable de capturer des virgules échappées lorsque d’autres méthodes sont en difficulté:

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

Exemple Live

Un piège notable avec les itérateurs de regex est que l'argument regex doit être une valeur L. Une valeur R ne fonctionnera pas .

Exemple de regex_iterator

Lorsque le traitement des captures doit être effectué itérativement, un regex_iterator est un bon choix. regex_iterator un regex_iterator renvoie un match_result . Ceci est idéal pour les captures conditionnelles ou les captures qui ont une interdépendance. Disons que nous voulons tokenize du code C ++. Donné:

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

On peut tokenize cette chaîne: const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s avec un regex_iterator comme ceci:

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

Exemple Live

Un argument notable avec les itérateurs de regex est que l'argument regex doit être une valeur L, une valeur R ne fonctionnera pas: Visual Studio regex_iterator Bug?

Fractionner une chaîne

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"

Quantificateurs

Disons que nous avons reçu const string input tant que numéro de téléphone à valider. Nous pourrions commencer par demander une entrée numérique avec un quantificateur nul ou plus : regex_match(input, regex("\\d*")) ou un ou plusieurs quantificateurs : regex_match(input, regex("\\d+")) Les deux ne sont vraiment pas corrects si l' input contient une chaîne numérique invalide telle que: "123" Utilisons un quantificateur n ou plus pour nous assurer que nous obtenons au moins 7 chiffres:

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

Cela garantira que nous aurons au moins un numéro de téléphone de chiffres, mais l' input pourrait également contenir une chaîne numérique trop longue comme: "123456789012". Laissons donc entre un quantificateur compris entre n et m, de sorte que l' input comporte au moins 7 chiffres mais pas plus de 11:

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

Cela nous rapproche, mais les chaînes numériques illégales comprises entre [7, 11] sont toujours acceptées, comme: "123456789" Rendons donc le code du pays facultatif avec un quantificateur différé :

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

Il est important de noter que le quantificateur paresseux correspond au moins de caractères possible , de sorte que la seule façon dont ce caractère sera comparé est de savoir si 10 caractères ont déjà été identifiés par \d{7,10} . (Pour faire correspondre le premier caractère avec avidité, nous aurions dû faire: \d{0,1} .) Le quantificateur paresseux peut être ajouté à tout autre quantificateur.

Maintenant, comment pourrions-nous rendre l'indicatif régional facultatif et accepter uniquement un code de pays si l'indicatif régional était présent?

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

Dans cette dernière regex, le \d{7} nécessite 7 chiffres. Ces 7 chiffres sont éventuellement précédés de 3 ou 4 chiffres.

Notez que nous n'avons pas ajouté le quantificateur paresseux : \d{3,4}?\d{7} , le \d{3,4}? aurait correspondu soit 3 ou 4 caractères, préférant 3. Au lieu de cela, nous faisons la correspondance de groupe non capturant au plus une fois, préférant ne pas correspondre. Causant une non-concordance si l' input ne comprenait pas l'indicatif régional tel que: "1234567".


En conclusion du sujet du quantificateur, j'aimerais mentionner l'autre quantificateur que vous pouvez utiliser, le quantificateur possessif . Le quantificateur différé ou le quantificateur possessif peuvent être ajoutés à tout quantificateur. La seule fonction du quantificateur possessif est d 'aider le moteur regex en lui disant, de prendre ces caractères avec avidité et de ne jamais les abandonner même si cela provoque l' échec de l 'expression rationnelle . Cela n'a pas beaucoup de sens, par exemple: regex_match(input, regex("\\d{3,4}+\\d{7})) car une input comme" 1234567890 "ne correspondrait pas à \d{3,4}+ correspondra toujours à 4 caractères même si la correspondance 3 aurait permis à l'expression rationnelle de réussir.
Le quantificateur possessif est mieux utilisé lorsque le jeton quantifié limite le nombre de caractères pouvant être associés . Par exemple:

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

Peut être utilisé pour faire correspondre si l’ input contient l’un des éléments suivants:

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

Mais quand cette regex brille vraiment, c'est quand l' input contient une entrée illégale :

12345 - 67890

Sans le quantificateur possessif, le moteur d'expressions rationnelles doit revenir en arrière et tester toutes les combinaisons de .* Et de 3 ou 4 caractères pour voir s'il peut trouver une combinaison compatible. Avec le quantificateur possessif, l'expression rationnelle commence là où le deuxième quantificateur possessif s'est arrêté, le caractère '0' et le moteur d'expression régulière essaie d'ajuster le .* Pour permettre à \d{3,4} de correspondre; Si le regex ne réussit pas, il n'y a pas de retour en arrière pour voir si un ajustement a été effectué auparavant .* .

Ancres

C ++ ne fournit que 4 ancres:

  • ^ qui affirme le début de la chaîne
  • $ qui affirme la fin de la chaîne
  • \b qui affirme un caractère \W ou le début ou la fin de la chaîne
  • \B qui affirme un caractère \w

Disons par exemple que nous voulons capturer un numéro avec son signe:

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

Exemple Live

Une note importante ici est que l'ancre ne consomme aucun caractère.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow