C++
Регулярные выражения
Поиск…
Вступление
Регулярные выражения (иногда называемые регулярными выражениями или регулярными выражениями ) представляют собой текстовый синтаксис, который представляет шаблоны, которые могут быть сопоставлены в используемых строках.
Регулярные выражения, введенные в 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 |
Основные примеры regex_match и 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
Этот код принимает различные стили скобок и преобразует их в 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+)" }));
}
Важно отметить, что якорь не потребляет никаких символов.