Литмир - Электронная Библиотека
A
A

\w2\d5–\d4

Однако на самом деле нам нужна какая-то синтаксическая конструкция, чтобы показать, что числа 2, 5 и 4 в этом шаблоне являются значениями счетчиков, не просто цифрами 2, 5 и 4. Выделим значения счетчиков фигурными скобками.

\w{2}\d{5}–\d{4}

Теперь символ { является таким же специальным символом, как и обратная косая черта, \, но этого избежать невозможно, и мы должны просто учитывать этот факт.

Итак, все бы ничего, но мы забыли о двух обстоятельствах: последние четыре цифры в почтовом коде ZIP являются необязательными. Иногда допустимыми являются оба варианта: TX77845 и TX77845–5629. Этот факт можно выразить двумя основными способами:

\w{2}\d{5} или \w{2}\d{5}–\d{4}

и

\w{2}\d{5} и необязательно –\d{4}

 

Программирование. Принципы и практика использования C++ Исправленное издание - _002.png
 Точнее говоря, сначала мы должны выразить идею группирования (или частичного шаблона), чтобы говорить о том, что строки \w{2}\d{5} и –\d{4} являются частями строки \w{2}\d{5}–\d{4}. Обычно группирование выражается с помощью круглых скобок.

(\w{2}\d{5})(–\d{4})

Теперь мы должны разбить шаблон на два частичных шаблона (sub-patterns), т.е. указать, что именно мы хотим с ними делать. Как обычно, введение новой возможности достигается за счет использования нового специального символа: теперь символ ( является специальным, как и символы \ и {. Обычно символ | используется для обозначения операции “или” (альтернативы), а символ ? — для обозначения чего-то условного (необязательного). Итак, можем написать следующее:

(\w{2}\d{5})|(\w{2}\d{5}–\d{4})

и

(\w{2}\d{5})(–\d{4})?

Как и фигурные скобки при обозначении счетчиков (например, \w{2}), знак вопроса (?) используется как суффикс. Например, (–\d{4})? означает “необязательно –\d{4}”; т.е. мы интерпретируем четыре цифры, перед которыми стоит дефис, как суффикс. На самом деле мы не используем круглые скобки для выделения пятизначного почтового кода ZIP (\w{2}\d{5}) для выполнения какой-либо операции, поэтому их можно удалить.

\w{2}\d{5}(–\d{4})?

Для того чтобы завершить наше решение задачи, поставленной в разделе 23.5, можем добавить необязательный пробел после двух букв.

\w{2} ?\d{5}(–\d{4})?

Запись “?” выглядит довольно странно, но знак вопроса после пробела указывает на то, что пробел является необязательным. Если бы мы хотели, чтобы пробел не выглядел опечаткой, то должны были бы заключить его в скобки.

\w{2}( )?\d{5}((–\d{4})?

Если бы кто-то сказал, что эта запись выглядит слишком неразборчивой, то нам пришлось бы придумать обозначение для пробела, например \s (s — от слова “space”). В этом случае запись выглядела бы так:

\w{2}\s?\d{5}(–\d{4})?

А что если кто-то поставит два пробела после букв? В соответствии с определенным выше шаблоном это означало бы, что мы принимаем коды TX77845 и TX 77845, но не TX 77845. Это неправильно.

Нам нужно средство, чтобы сказать “ни одного, один или несколько пробелов”, поэтому мы вводим суффикс *.

\w{2}\s*\d{5}(–\d{4})?

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Было бы целесообразно выполнять каждый этап в строгой логической последовательности. Эта система обозначения логична и очень лаконична. Кроме того, мы не принимали проектные решения с потолка: выбранная нами система обозначений очень широко распространена. При решении большинства задач, связанных с обработкой текста, нам необходимо читать и записывать эти символы. Да, эти записи похожи на результат прогулки кошки по клавиатуре, и ошибка в единственном месте (наш лишний или пропущенный пробел) полностью изменяет их смысл, но с этим приходится смириться. Мы не можем предложить ничего радикально лучшего, и этот стиль обозначений за тридцать лет распространился очень широко. Впервые он был использован в команде
grep
в системе Unix, но и даже тогда его нельзя было назвать совершенно новым.

23.7. Поиск с помощью регулярных выражений

Теперь применим шаблон почтовых кодов ZIP из предыдущего раздела для поиска почтовых кодов в файле. Программа определяет шаблон, а затем ищет его, считывая файл строка за строкой. Когда программа находит шаблон в какой-то строке, она выводит номер строки и найденный код.

#include <boost/regex.hpp>

#include <iostream>

#include <string>

#include <fstream>

using namespace std;

int main()

{

  ifstream in("file.txt");       // файл ввода

  if (!in) cerr << "нет файла \n";

  boost::regex pat ("\\w{2}\\s*\\d{5}(–\\d{4})?"); // шаблон

                                                   // кода ZIP

  cout << "шаблон: " << pat << '\n';

  int lineno = 0;

  string line;                  // буфер ввода

  while (getline(in,line)) {

    ++lineno;

    boost::smatch matches;        // записываем сюда совпавшие строки

    if (boost::regex_search(line, matches, pat))

      cout << lineno << ": " << matches[0] << '\n';

  }

}

Эта программа требует объяснений. Сначала рассмотрим следующий фрагмент:

#include <boost/regex.hpp>

...

boost::regex pat ("\\w{2}\\s*\\d{5}(–\\d{4})?");  // шаблон кода ZIP

boost::smatch matches;           // записываем сюда совпавшие строки

323
{"b":"847443","o":1}