Допустим, нам нужно распарсить строку с диапазонами страниц для печати, разделенными запятыми. Строка типа "1, 2, 4-7, 10". Первое, что пришло в голову:
using namespace std; string str = " 1 , 2, 4 , 3 - 7 , 9 - 11"; //отдельная цифра может быть окружена пробелами со всех сторон const string numb = "[\\s]*[0-9]+[\\s]*"; //диапазон -это либо одна цифра, либо две цифрыЮ разделенные тире const string diapason = numb + "(\\-" + numb + "){0,1}"; const std::tr1::regex regex_diap(diapason); //выводим все подстроки, соответствующие паттерну const tr1::sregex_token_iterator end; tr1::sregex_token_iterator iter = tr1::sregex_token_iterator(str.begin(), str.end(), regex_diap); for (; iter != end; ++iter) std::cout << *iter << std::endl;В результате программа выведет:
1
2
4
7 - 3
8 - 11
Все хорошо до тех пор, пока мы не попробуем вместо правильной строки ввести, например, такую неправильную строку: " 1 , 2, 4 , 3aaa - 7 , 9 - 11". В этом случае программа выведет следующее:
1
2
4
3
7
9 - 11
То есть программа распознала неправильный диапазон, как два отдельных числа. Нас такой результат не устраивает. Значит, надо указать, что диапазон должен содержать только цифры, пробелы и минус между цифрами. Порывшись в гугле, нашел такой вариант решения:
using namespace std; //входная строка string str = " 1 , 2, 4 , 5 - 7, 9 - 11"; //целое число, которое может быть обрамлено пробельными символами const string numb = "[\\s]*[0-9]+[\\s]*"; //вот тут мы используем т.н. просмотр вперёд и назад const string one_page = "(?<=(,|^))" + numb + "(?=(,|$))"; const string many_page = "(?<=(,|^))" + numb + "\\-" + numb + "(?=(,|$))"; //число или диапазон const string one_many_page = "(" + one_page + "|" + many_page + ")"; const tr1::regex rx(one_many_page); const tr1::sregex_token_iterator end; tr1::sregex_token_iterator it; for (it = tr1::sregex_token_iterator(str.begin(), str.end(), rx); it != end; ++it) std::cout << *it << std::endl;Самое интересное тут - это т.н. просмотр вперед и назад без захвата выражения. Подробнее - в википедии. Благодаря чему мы указываем, что любому отдельному числу должна предшествовать либо запятая, либо начало строки, отделенные нулем или большим числом пробелов и за ним должна следовать либо запятая, либо конец строки, отделенные нулем или большим числом пробелов:
const string numb = "[\\s]*[0-9]+[\\s]*"; const string one_page = "(?<=(,|^))" + numb + "(?=(,|$))";Аналогично с диапазоном чисел:
const string numb = "[\\s]*[0-9]+[\\s]*"; сonst string many_page = "(?<=(,|^))" + numb + "\\-" + numb + "(?=(,|$))";Ну и регулярное выражение, которое выбирает ИЛИ число ИЛИ диапазон:
const string one_many_page = "(" + one_page + "|" + many_page + ")";Теперь, если указать неверный диапазон чисел типа " 1 , 2, 4 , 7aaa - 3, 8 - 11", то на выходе получим:
1
2
4
8 - 11
То есть неверный диапазон мы просто пропустили, вместо неверной его интерпретации, что и требовалось.
Для подсветки синтаксиса использовал highlight.hohli.com
tr1::regex не поддерживает просмотра назад (lookbehind assertions). Как у вас получилось? Или вы лукавите?
ОтветитьУдалить