Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
В этой программе требуется часть имени перед точкой, что является первым подвыражением, поэтому следует вывести results.str(1).
Подвыражения для проверки правильности данныхПодвыражения обычно используются для проверки данных, которые должны соответствовать некоему определенному формату. Например, в Америке номера телефонов имеют десять цифр, включая код города и местный номер из семи цифр. Код города зачастую, но не всегда, заключен в круглые скобки. Остальные семь цифр могут быть отделены тире, точкой или пробелом либо не отделяться вообще. Данные в некоторых из этих форматов могли бы быть приемлемы, а в других — нет. Процесс будет состоять из двух этапов: сначала используем регулярное выражение для поиска последовательностей, которые могли бы быть номерами телефонов, а затем вызовем функцию для окончательной проверки правильности данных.
Прежде чем написать схему номеров телефона, необходимо рассмотреть еще несколько аспектов языка регулярных выражений на языке ECMAScript.
• {d} представляет одиночную цифру, а {d}{n} — последовательность из n цифр. (Например, {d}{3} соответствует последовательности из трех цифр.)
• Набор символов в квадратных скобках позволяет задать соответствие любому из трех символов. (Например, [-.] соответствует тире, точке или пробелу. Обратите внимание: у точки в квадратных скобках нет никакого специального смысла.)
• Компонент, следующий за символом '?', не обязательный. (Например, {d}{3}[-. ]?{d}{4} соответствует трем цифрам, сопровождаемым опциональными тире, точкой или пробелом и еще четырьмя цифрами. Этой схеме соответствовало бы 555-0132, или 555.0132, или 555 0132, или 5550132).
• Как и в языке С++, в ECMAScript символ за наклонной чертой означает, что он представляет себя, а не специальное значение. Поскольку данная схема включает круглые скобки, являющиеся специальными символами в языке ECMAScript, круглые скобки, являющиеся частью схемы, следует представить как ( или ).
Поскольку наклонная черта влево является специальным символом в языке С++, когда он встречается в схеме, следует добавить вторую наклонную черту, чтобы указать языку С++, что имеется в виду символ . Следовательно, чтобы представить регулярное выражение {d}{3}, нужно написать \{d}{3}.
Для проверки номеров телефонов следует обратиться к компонентам схемы. Например, необходимо проверить, что если номер использует открывающую круглую скобку для кода города, то он использует также закрывающую скобку после него. В результате такой номер, как (908.555.1800, следует отклонить.
Для определения такого соответствия необходимо регулярное выражение, использующее подвыражения. Каждое подвыражение заключается в пару круглых скобок:
// все выражение состоит из семи подвыражений: (ddd) разделитель ddd
// разделитель dddd
// подвыражения 1, 3, 4 и 6 опциональны; а 2, 5 и 7 содержат цифры
"(\()?(\d{3})(\))?([-. ])?(\d{3})([-. ]?)(\d{4})";
Поскольку схема использует круглые скобки, а также из-за использования наклонных черт, эту схему трудно прочитать (и написать!). Проще всего прочитать ее по каждому отдельному (заключенному в скобки) подвыражению.
1. (\()? необязательная открывающая скобка для кода города.
2. (\d{3}) код города.
3. (\))? необязательная закрывающая скобка для кода города.
4. ([-. ])? необязательный разделитель после кода города.
5. (\d{3}) следующие три цифры номера.
6. ([-. ])? другой необязательный разделитель.
7. (\d{4}) последние четыре цифры номера.
Следующий код использует эту схему для чтения файла и находит данные, соответствующие общей схеме телефонных номеров. Для проверки допустимости формата номеров используется функция valid():
string phone =
"(\()?(\d{3})(\))?([-. ])?(\d{3})([-. ]?)(\d{4})";
regex r(phone); // объект regex для поиска схемы
smatch m;
string s;
// прочитать все записи из входного файла
while (getline(cin, s)) {
// для каждого подходящего номера телефона
for (sregex_iterator it(s.begin(), s.end(), r), end_it;
it != end_it; ++it)
// проверить допустимость формата номера
if (valid(*it))
cout << "valid: " << it->str() << endl;
else
cout << "not valid: " << it->str() << endl;
}
Операции с типом соответствияНапишем функцию valid(), используя операции типа соответствия, приведенные в табл. 17.11. Не следует забывать, что схема pattern состоит из семи подвыражений. В результате каждый объект класса smatch будет содержать восемь элементов ssub_match. Элемент [0] представляет общее соответствие, а элементы [1] - [7] представляют каждое из соответствующих подвыражений.
Таблица 17.11. Операции с типом соответствия
Эти операции применимы к типам ssub_match, csub_match, wssub_match и wcsub_match matched Открытая логическая переменная-член, означающая соответствие объекта класса ssub_match first second Открытые переменные-члены, являющиеся итераторами на начало последовательности соответствия и ее следующий элемент после последнего. Если соответствия нет, то first и second равны length() Размер текущего объекта соответствия. Возвращает 0, если переменная-член matched содержит значение false str() Возвращает строку, содержащую соответствующую часть ввода. Возвращает пустую строку, если переменная-член matched содержит значение false s = ssub Преобразует объект ssub класса ssub_match в строку s. Эквивалент вызова s = ssub.str(). Оператор преобразования не является явным (см. раздел 14.9.1)Когда происходит вызов функции valid(), известно, что общее соответствие имеется, но неизвестно, какие из необязательных подвыражений являются частью этого соответствия. Переменная-член matched класса ssub_match, соответствующая определенному подвыражению, содержит значение true, если это подвыражение является частью общего соответствия.
В правильном номере телефона код города либо полностью заключается в скобки, либо не заключается в них вообще. Поэтому действие функции valid() зависит от того, начинается ли номер с круглой скобки или нет:
bool valid(const smatch& m) {
// если перед кодом города есть открывающая скобка
if (m[1].matched)
// за кодом города должна быть закрывающая скобка
// и остальная часть номера непосредственно или через пробел
return m[3].matched
&& (m[4].matched == 0 || m[4].str() == " ");
else
// здесь после кода города не может быть закрывающей скобки
// но разделители между другими двумя компонентами должны быть
// корректны
return !m[3].matched
&& m[4].str() == m[6].str();
}
Начнем с проверки соответствия первому подвыражению (т.е. открывающей скобки). Это подвыражение находится в элементе m[1]. Если это соответствие есть, то номер начинается с открывающей скобки. В таком случае номер будет допустимым, только если подвыражение после кода города также будет соответствующим (т.е. будет закрывающая скобка после кода города). Кроме того, если скобки в начале номера корректны, то следующим символом должен быть пробел или первая цифра следующей части номера.
Если элемент m[1] не соответствует (т.е. открывающей скобки нет), то подвыражение после кода города также должно быть пустым. Если это так и если остальные разделители совпадают, то номер допустим, но не в противном случае.
Упражнения раздела 17.3.3Упражнение 17.19. Почему можно вызывать функцию m[4].str() без предварительной проверки соответствия элемента m[4]?
Упражнение 17.20. Напишите собственную версию программы для проверки номеров телефонов.
Упражнение 17.21. Перепишите программу номеров телефонов из раздела 8.3.2 так, чтобы использовать функцию valid(), определенную в этом разделе.