Поиск…


Вступление

Регулярные выражения (иногда называемые регулярными выражениями или регулярными выражениями ) представляют собой текстовый синтаксис, который представляет шаблоны, которые могут быть сопоставлены в используемых строках.

Регулярные выражения, введенные в c ++ 11 , могут опционально поддерживать возвращаемый массив совпадающих строк или другой текстовый синтаксис, определяющий, как заменить сопоставленные шаблоны в строках, на которых работает.

Синтаксис

  • regex_match // Возвращает, была ли вся последовательность символов сопоставлена ​​регулярным выражением, при желании, захват в объект соответствия
  • regex_search // Возвращает, соответствует ли последовательность символов символу регулярным выражением, необязательно, захватывая объект соответствия
  • regex_replace // Возвращает последовательность символов ввода, модифицированную регулярным выражением, с помощью строки замещающего формата
  • regex_token_iterator // Инициализируется символьной последовательностью, определенной итераторами, списком индексов захвата для итерации и регулярным выражением. Dereferencing возвращает текущее индексированное соответствие регулярного выражения. Приращение шагов к следующему индексу захвата или, если он находится в последнем индексе, сбрасывает индекс и задерживает следующее вхождение регулярного выражения в последовательности символов
  • regex_iterator // Инициализируется символьной последовательностью, определенной итераторами и регулярным выражением. Выделение возвращает часть последовательности символов, совпадающей со всем регулярным выражением. Приращение находит следующее вхождение регулярного выражения в последовательности символов

параметры

Подпись Описание
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) BidirectionalIterator - это любой итератор символов, который обеспечивает приращения и уменьшения операторов smatch может быть cmatch или любым другим другим вариантом match_results который принимает тип BidirectionalIterator аргумент smatch может быть пропущен, если результаты регулярного выражения не нужны. Возвращает, соответствует ли re полный символ последовательность, определенная first и last
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) string может быть либо const char* либо string L-Value, функции, принимающие string R-значения, явно удалены. smatch может быть cmatch или любым другим другим вариантом match_results который принимает тип str аргумент smatch может быть опущен, если результаты регулярного выражения не нужны. Возвращает, соответствует ли re всей последовательности символов, определяемой 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"
    }
}

Живой пример

Пример regex_replace

Этот код принимает различные стили скобок и преобразует их в 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;

Живой пример

Пример regex_token_iterator

std::regex_token_iterator предоставляет потрясающий инструмент для извлечения элементов файла Comma Separated Value . Помимо преимуществ итерации, этот итератор также способен захватывать скрытые запятые, в которых работают другие методы:

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

Живой пример

Заметная gotcha с итераторами регулярных выражений состоит в том, что аргумент regex должен быть L-значением. Значение R не будет работать .

Пример regex_iterator

Когда обработка захватов должна выполняться итеративно, regex_iterator является хорошим выбором. Выделение regex_iterator возвращает match_result . Это отлично подходит для условных захватов или захватов, которые имеют взаимозависимость. Предположим, что мы хотим подделать некоторый код на C ++. Дано:

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

Мы можем tokenize эту строку: const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s с 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);
}

Живой пример

Заметная gotcha с итераторами регулярных выражений состоит в том, что аргумент regex должен быть L-значением, R-значение не будет работать: Visual Studio regex_iterator Bug?

Разделение строки

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"

Кванторы

Предположим, что нам предоставляется const string input в качестве номера телефона для проверки. Мы могли бы начать с ввода числового ввода с нулевым или более regex_match(input, regex("\\d*")) : regex_match(input, regex("\\d*")) или один или несколько квантификаторов : regex_match(input, regex("\\d+")) Но оба из них действительно бывают короткими, если input содержит недопустимую числовую строку, например: «123». Давайте использовать n или более квантификаторов, чтобы гарантировать, что мы получаем как минимум 7 цифр:

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

Это гарантирует, что мы получим по крайней мере номер телефона, но input также может содержать цифровую строку, которая слишком длинна: ​​«123456789012». Так что давайте перейдем с квантором между n и m, так что input будет не менее 7 цифр, но не более 11:

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

Это приближает нас, но незаконные числовые строки, которые находятся в диапазоне [7, 11], все еще принимаются, например: «123456789». Давайте сделаем код страны необязательным с ленивым квантиром :

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

Важно отметить, что ленивый квантификатор соответствует как можно большему количеству символов , поэтому единственный способ сопоставления этого символа - если у вас уже есть 10 символов, которые были сопоставлены \d{7,10} . (Чтобы совместить первый символ с жадностью, нам пришлось бы делать: \d{0,1} .) Ленивый квантификатор может быть добавлен к любому другому квантификатору.

Теперь, как бы мы сделали код области необязательным и принимали только код страны, если код области присутствовал?

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

В этом финальном регулярном выражении \d{7} требуется 7 цифр. Этим 7 цифрам необязательно предшествуют либо 3, либо 4 цифры.

Обратите внимание, что мы не добавляли ленивый квантификатор : \d{3,4}?\d{7} , \d{3,4}? соответствовали бы 3 или 4 символам, предпочитая 3. Вместо этого мы собираем не-захват группового совпадения не более одного раза, предпочитая не совпадать. Вызвать несоответствие, если input не включал код зоны, например: «1234567».


В заключение темы квантора я хотел бы упомянуть о другом квантовом квантовании, который вы можете использовать, притяжательный квантификатор . Либо ленивый квантор или притяжательное квантор может быть добавлен к любому квантора. Единственная функция приходящего квантификатора - помочь движку регулярных выражений, сообщая ему, жадно воспринимать эти символы и никогда не отказываться от них, даже если это приводит к сбою регулярного выражения . Это, например, не имеет особого смысла: regex_match(input, regex("\\d{3,4}+\\d{7})) Поскольку input типа" 1234567890 "не будет соответствовать как \d{3,4}+ всегда будет соответствовать 4 символам, даже если совпадение 3 позволило бы регулярному выражению преуспеть.
Притяжательный квантификатор лучше всего использовать, когда квантованный токен ограничивает количество подходящих символов . Например:

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

Может использоваться для соответствия, если input содержит любое из следующего:

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

Но когда это регулярное выражение действительно сияет, когда input содержит недопустимый вход:

12345 - 67890

Без притяжательного квантификатора двигатель регулярных выражений должен возвращаться и проверять каждую комбинацию .* И 3 или 4 символа, чтобы увидеть, может ли он найти подходящую комбинацию. С помощью притяжательного квантификатора регулярное выражение начинается там, где остался 2- й обладающий квантификатор , символ «0» и механизм регулярных выражений пытаются настроить .* Чтобы позволить \d{3,4} соответствовать; когда он не может просто повторять регулярное выражение, отслеживание обратной связи не выполняется, чтобы увидеть, если раньше .* Настройка могла позволить совпадение.

Якоря

C ++ обеспечивает только 4 привязки:

  • ^ который утверждает начало строки
  • $ который утверждает конец строки
  • \b который утверждает символ \W или начало или конец строки
  • \B который утверждает символ \w

Скажем, например, мы хотим захватить число с его знаком:

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

Живой пример

Важно отметить, что якорь не потребляет никаких символов.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow