Книга широко известных авторов, разработчиков языка Си, переработанная и дополненная с учетом стандарта ANSI для языка Си, 2-е английское издание которой вышло в 1988 году, давно стала классикой для всех изучающих и/или использующих как Си, так и Си++. Русский перевод этой книги впервые был выпущен изд-вом "Финансы и статистика" в 1992 г. и с тех пор пользуется неиз- менным спросом читателей. Для настоящего третьего русского издания перевод заново сверен с оригина- лом, в него внесены некоторые поправки, учитывающие устоявшиеся за прошед- шие годы изменения в терминологии. Для программистов, преподавателей и студентов.
Эта книга представляет собой перевод третьего издания американского бестселлера Effective C++ и является руководством по грамотному использованию языка C++. Она поможет сделать ваши программы более понятными, простыми в сопровождении и эффективными. Помимо материала, описывающего общую стратегию проектирования, книга включает в себя главы по программированию с применением шаблонов и по управлению ресурсами, а также множество советов, которые позволят усовершенствовать ваши программы и сделать работу более интересной и творческой. Книга также включает новый материал по принципам обработки исключений, паттернам проектирования и библиотечным средствам.
Издание ориентировано на программистов, знакомых с основами C++ и имеющих навыки его практического применения.
Чтобы упростить ориентирование во все более разрастающемся Интернете, была разработана система DNS (Domain Name System - система именования доменов сети). Дело в том, что каждому компьютеру или компьютерной сети, подключенной к Интернету, назначается уникальная последовательность цифр, называемая IP-адресом.
IP-адрес состоит из четырех чисел, от 0 до 255 каждое, например 198.105.232.001. Зная IP-адрес, пользователь одного компьютера с легкостью находит другой компьютер в Интернете, и может к нему подключиться, если у него есть на это соответствующие права. Все просто, когда вам нужно получать доступ к одному-двум компьютерам, но если их количество переваливает за десяток или даже за сотню, а, тем более, если вам необходимо сообщать определенный IP-адрес многим людям, ситуация становится поистине кошмарной.
Избавиться от подобных проблем помогает система имен DNS. Она позволяет заменять цифровые IP-адреса на благозвучные буквенные, например: «microsoft.com» или «yandex.ru». Как же работает DNS? Все Интернет-пространство можно разделить на несколько групп, называемых «доменными зонами». Эти зоны называются доменами первого уровня. Разделение по зонам может проводиться как по географическому, так и по тематическому признаку. Географическая доменная зона определяет расположение компьютера в том или ином государстве. Вот несколько примеров географических доменов первого уровня: ru - Россия, fr - Франция, uk - Великобритания, jp - Япония, su - бывший Советский Союз. Тематические доменные зоны группируют компьютеры по информации, содержащейся на них, либо по типу организаций, ими владеющих, вне зависимости от их географического расположения.
Два компьютера, зарегистрированные в одной тематической доменной зоне, могут находиться в противоположных концах земного шара. Вот примеры тематических доменных зон: com - коммерческое предприятие, net - что-то связанное с сетевыми технологиями, edu - образовательное учреждение, info - информационный проект, gov - государственное учреждение, biz - бизнес-проект, mil - военная организация. Несмотря на обилие доменных зон, далеко не все из них пользуются большой популярностью. Основная часть компьютеров в Интернете зарегистрирована в доменных зонах com и net. Некоторые доменные зоны используются и вовсе не по прямому назначению. Например, островное государство Тувалу стало обладателем географической доменной зоны tv, которую сейчас облюбовали организации, так или иначе связанные с телевидением: телеканалы, производители бытовой техники, киноделы, рекламщики и прочие...
Каждая доменная зона делится на поддомены, или домены второго уровня, и каждому из этих поддоменов присваивается свое имя, например совпадающее с названием организации, владеющей доменом. Это имя приписывается к имени домена верхнего уровня слева, в виде суффикса, и отделяется точкой. Например, в имени microsoft.com строка com означает доменную зону, а суффикс microsoft - имя домена второго уровня. Как нетрудно догадаться, по этому адресу находится сеть, принадлежащая корпорации Microsoft. Однако сеть корпорации Microsoft весьма велика, поэтому каждый домен второго уровня, в свою очередь, может делиться еще на несколько подподдоменов, или доменов третьего уровня. Это записывается так - mail.microsoft.com. В этом примере mail - это суффикс домена третьего уровня. Такое деление может продолжаться до бесконечности, но обычно ограничивается доменами третьего-четвертого уровня.
Общее руководство и контроль над доменными зонами, осуществляет организация ICANN (The Internet Corporation for Assigned Names and Number - Интернет-ассоциация по выдаче имен и чисел). Она передает полномочия на выдачу адресов в той или иной доменной зоне другим организациям и следит за соблюдением основных правил. Организации, уполномоченные выдавать доменные адреса в той или иной доменной зоне, торгуют доменными адресами второго уровня. То есть, если кто-то хочет, чтобы у его компьютера в Интернет был адрес vasya-pupkin.com, он должен обратиться к организации, выдающей доменные имена в зоне com. Затем попросить зарегистрировать в ней домен второго уровня vasya-pupkin, предоставить IP-адрес своего компьютера в Сети и, разумеется, уплатить некоторую сумму денег. В результате, компьютер Васи в Интернете можно будет отыскать не только по малопонятному набору цифр IP-адреса, но и по звучному текстовому адресу.
При желании, одному IP-адресу можно сопоставить даже несколько доменных имен, например vasya-pupkin.com и vasiliy.ru. Адреса в Российской доменной зоне выдает организации РосНИИРОС, Российский НИИ развития общественных сетей.
Современный Интернет представляет собой сложнейшую систему из тысяч компьютерных сетей, объединенных между собой. Состоит эта система из двух основных элементов: узлов сети Интернет и соединяющих их информационных магистралей. Узлом Интернета называют любое устройство, имеющее свой IP-адрес и подключенное к Сети. Несмотря на кажущуюся мешанину межкомпыотерных соединений и отсутствие централизованного руководства, Интернет имеет определенную иерархическую структуру.
В самом низу иерархии находится многочисленная армия конечных пользователей. Часто не имеющие даже постоянного IP-адреса подключаются к Интернету по низкоскоростным каналам. Тем не менее, пользователи являются одними из основных потребителей услуг Сети и главными «спонсорами» коммерческой части Интернета. Причем на одного «физического» пользователя, т. е. реального человека, пользующегося услугами Сети, может приходиться несколько пользователей «логических», т. е. различных подключений к Интернету.
Так, кроме компьютера, возможность подключения к Интернету может иметь мобильный телефон, карманный компьютер, бытовая техника, автомобиль и даже кондиционер. Конечные пользователи подключаются к компьютерам Интернет-провайдера, или, как их еще называют, ISP (Internet Service Provider - провайдер Интернет). ISP - это организация, основная деятельность которой связана с предоставлением услуг Интернета пользователям.
У провайдера есть своя компьютерная сеть, размеры которой могут варьироваться от сотен десятков узлов в нескольких городах до многих тысяч, раскиданных по целому континенту. Эта сеть называется магистральной сетью, или бэкбоном (от слова backbone - стержень, магистраль). Сети отдельных провайдеров соединяются между собой и другими сетями. Среди ISP есть «монстры», которые обеспечивают соединение между собой сетей различных стран и континентов, являясь своего рода «провайдерами для провайдеров». Весь этот конгломерат компьютерных сетей и образует то, что называется Интернетом.
Особняком стоят DNS-серверы - компьютеры, отвечающие за функционирование системы DNS. Для подключения конечных пользователей к ISP служат так называемые «точки доступа» - компьютеры или специальные устройства, содержащие оборудование для подключения «извне».
Подключившись к точке доступа провайдера, пользователь становится частью магистральной сети провайдера и, соответственно, получает доступ к ее ресурсам, а также к ресурсам сетей, соединенных с бэкбоном провайдера, т. е. ко всему Интернету. Кроме конечных пользователей, к сети провайдеров подключаются различного рода серверы, или «хосты» (от слова host - хозяин). Это узлы сети, на которых работает программное обеспечение, обеспечивающее практически все услуги, предоставляемые сетью Интернет.
Поскольку качество видео на DVD носителях превосходное, то вопрос защиты от копирования стоит острее, чем защита от копирования фильмов на VCD и видеокассета. Может показаться, что вообще невозможно предотвратить незаконное копирование как цифровых так и аналоговых форматов и в любом случае найдутся "умельцы". Но все же принимаются меры. Какие мы вам расскажем далее.
Механизм защиты от копирования DVD
Во-первых, давайте посмотрим сколько дорожек доступно для копирования в DVD системе. Первая дорожка содержит необрабатываемые цифровые данные, считываемые с DVD привода, в возможные пиратские приборы встроены DVD видео декодеры, которые не будут принимать меры против защиты от копирования на дорожках 2 и 3. Система content scramble system (CSS) не позволяет добраться до содержания 2 и 3 дорожки без чтения первой. Сигнал со второй дорожки идет в аналоговом телевизионном формате NTSC или PAL. Поскольку VHS видеомагнитофоны очень распространены на сегодняшний день, то проще всего сделать копию в этом формате с DVD качеством.
content scramble system (система защиты от копирования)
основной целью CSS является защита содержания DVD от пиратского взлома и копирования через защиту от DVD видео декодеров и дисководов перезаписываемых дисков. Чтобы воспроизвести защищенный авторским правом материал с DVD ROM диска нужно согласие владельца авторского права, для чего и создана система content scramble. Три кода нанесены один за другим, что значит, что второй ключ может быть получен только при обладании первым, а третий только через получение второго. После этого, сжатое содержание может быть развернуто посредством третьего ключа. То есть для полного доступа нужно иметь три ключа. Конечно, алгоритм расшифровки можно получить через подписание документов, разрешающих тиражирование. Для предотвращения копирования с/на цифровые носители в среде персонального компьютера, предпринята попытка идентификации и шифровки данных. В среде персонального компьютера, для копирования необходимо два "компонента": DVD ROM привод и карта декодера, подсоединенные к PC шине. Поскольку данные с PC шины легко скопировать, то DVD ROM должен сам проверять законность получателя перед отправки данных. Также, для предотвращения воспроизведения нелегально скопированного материала, карта декодера должна проверять законность отправителя данных. Поэтому необходима обоюдная идентификация. А для предотвращения подмены диска после идентификации, DVD ROM привод должен периодически менять ключ шифра перед отсылкой.
Macrovision & CGMS/A (copy generation management system/analog (макровижн и система управления тиражированием/аналоговая))
Макровижн основан на различиях в работе видеомагнитофонов и телевизоров. Защита от копирования в этом случае состоит из двух элементов: AGC [Automatic Gain Control] автоматическая регулировка усиления и "полосатого" кода. Система AGC в телевизоре спроектирована так, что медленно реагирует на изменения, та же, схема, которая встроена в видеомагнитофоны, должна мгновенно реагировать на изменения. Именно это различие и лежит в основе системы. Суть в том, что макровижн изменяет сигнал так, что при воспроизведении картинка будет хорошей, а при записи на копии будут множественные качественные изменения. Что касается "полосатого" кода, то при воспроизведении он не оказывает никакого влияния на качество изображения, при просмотре копии на картинке появится ужасная вертикальная полосность.
В то время как макровижн направлен на устранение пиратских копий, CGMS/A направлена на контроль записи легальных копий. Информация CGMS/A вложена в выходящий видео сигнал. Для работы CGMS/A ( то есть для возможности сделать легальную копию) необходимо, чтобы записывающее оборудование распознавало CGMS. CGMS кодирует информацию на линии 21 системы NTSC, при этом CGMS имеет приоритет над антикопировальными сигналами макровижн, записываемых на ту же линию.
CGMS/D (система управления тиражированием/цифровая)
Эта система основана на стандарте IEEE 1394 и предназначена для ограничения ("copy once"- одна копия) и запрещение ("copy never"- запрещение копирования) создания цифровых копий. Цифровые приборы, такие, например как DVD плеер и цифровой TV, будут обмениваться ключами и идентификационными подтверждениями перед установлением канала. DVD плеер шифрует видео сигнал при отправке, а получающий прибор расшифровывает его. Пишущие цифровые приборы не смогут получать сигнал при внутренней маркировке "copy never", а при маркировке "copy once"- сделают копию и изменят маркер на "copy never". CGMS/D спроектирован для следующего поколения цифровых ТВ и видео рекордеров. Для этой системы нужны DVD проигрыватели нового поколения с цифровыми соединениями.
Код региона (код места)
Смысл этого кодирования состоит в том, что киностудии пожелали ввести дополнительную кодировку поскольку в большинстве случаев фильмы вышедшие на DVD в одной стране еще идут на киноэкране другой страны. Для увеличения доходов от проката фильмов для разных географических регионов устанавливаются разные коды. Этот код будет встраиваться в DVD проигрыватель на основании региона в котором он был продан. Это означает, что диски купленные в одной стране могут не проигрываться в другой.
Регионы разбиты так. Каждый диск будет идентифицироваться по цифре. Если диск разрешен к проигрыванию в более чем одном регионе, то соответственно и цифр будет больше.
1: Канада, США, Территории США.
2: Япония, Европа, Южная Африка, Средний Восток (включая Египет)
3: Южно-восточная Азия, Восточная Азия (включая Гонконг)
4: Австралия, Новая Зеландия, Центральная Америка, Мексика, Южная Америка, Карибы.
5: Бывший Советский Союз, Индия и Африка, Северная Корея, Монголия
6: Китай
Поиск по шаблону является настолько обычным занятием в разработке программного обеспечения, что для облегчения этой задачи была создана специальная технология — регулярные выражения. Узнайте, как можно использовать ее при написании кода, прочитав эту статью.
Все устройства получают входную информацию, выполняют какие-либо операции и выдают результат. Например, телефон во время разговора преобразует звуковую энергию в электрический сигнал и обратно. Двигатель потребляет топливо (пар, расщепление атомных ядер, бензин, мышечные усилия) и преобразует его в энергию. Блендер поглощает ром, лед, лайм и кюрасао и взбалтывает их в коктейль Mai Tai. (Или, если вам хочется чего-то изысканного, сделайте Bellini из шампанского и грушевого сока. Блендер – замечательное универсальное устройство.)
Так как программное обеспечение преобразует данные, то каждое приложение фактически является устройством (хоть и виртуальным, так как у него нет физических составляющих). Например, компилятор в качестве входной информации получает исходную программу и преобразует ее в двоичный исполняемый код. Программа прогнозирования погоды генерирует предсказания на основе результатов прошлых (исторических) замеров, а графический редактор обрабатывает пикселы, применяя правила к отдельным пикселам или их группам, чтобы, например, сделать изображение более четким или изменить его стиль.
Так же, как и любое другое устройство, программное обеспечение предназначено для работы с определенным исходным материалом, например, набором чисел, данными XML-схемы или протоколом. Если программе задать некорректную входную информацию — неподходящую по форме или типу, то существует большая вероятность того, что результат будет непредсказуемым и, возможно, даже катастрофическим. Как говорится: "Мусор заложишь - мусор получишь".
На самом деле для решения всех нетривиальных задач необходимо отделять правильные данные от некорректных и отклонять некорректные данные во избежание ошибок в результатах. Это, конечно же, актуально и для Web-приложений, написанных на языке PHP. Неважно, получены ли входные данные из формы для ввода с клавиатуры или в результате выполнения программного запроса Asynchronous JavaScript + XML (Ajax), прежде чем начать какие-либо вычисления, программа должна проверить входную информацию. Возможно, что числовые значения должны находиться в пределах определенного диапазона чисел или представлять собой только целые числа. Возможно, значение должно соответствовать определенному формату, например, почтового индекса. Например, почтовый индекс в США представляет собой пять цифр плюс дополнительный префикс "Plus 4", состоящий из дефиса и 4 дополнительных цифр. Возможно, другие строки также должны состоять из определенного количества символов, например, две буквы для указания аббревиатуры штата США. Строковые данные доставляют особенно много проблем: PHP-приложение должно быть начеку по отношению к злонамеренным программам-агентам, вложенным в SQL-запросы, код JavaScript или любой другой код, которые способны изменить поведение приложения или обойти защиту.
Однако каким образом программа может определить, являются ли входные данные числом или соответствуют ли определенным требованиям, например, к почтовому индексу? На самом деле для реализации проверки путем сопоставления с шаблоном необходим небольшой парсер, создающий конечный автомат, считывающий входные данные, обрабатывающий маркеры, отслеживающий состояние и выдающий результаты. Однако создание и обслуживание даже самого простого парсера может оказаться непростым делом.
К счастью, анализ на основе сопоставления с шаблоном настолько широко распространен в компьютерных технологиях, что с течением времени (примерно с момента появления UNIX®) были разработаны специальные технологии и, конечно же, механизмы обработки, чтобы облегчить рутинную работу. Регулярное выражение (regex) описывает шаблоны посредством лаконичных и удобочитаемых обозначений. Получив регулярное выражение и данные, механизм regex сообщает, совпадают ли эти данные с шаблоном, и если совпадение было обнаружено, что именно совпало.
Вот небольшой пример использования регулярного выражения, взятый из UNIX-утилиты, работающей в режиме командной строки, которая ищет заданный шаблон в содержимом одного или нескольких текстовых файлов UNIX. Команда grep -i -E '^Bat' ищет последовательность символов beginning-of-line (начало строки), обозначаемое "крышкой", [^]), за которым следуют буквы b, a, и t верхнего или нижнего регистра (ключ -i указывает на то, что при сопоставлении с шаблоном регистр не учитывается, таким образом, например, B и b - тождественны). Следовательно, для файла heroes.txt:
Листинг 1. heroes.txt
Вышеупомянутая команда grep выдаст два совпадения:
Batman
Batgirl
Регулярные выражения
PHP предлагает два программных интерфейса регулярных выражений: один -- для интерфейса переносимых операционных систем (POSIX), а второй - для регулярных выражений, совместимых с языком Perl (PCRE). В общем и целом второй интерфейс является более предпочтительным, так как PCRE сам по себе мощнее, чем POSIX, и предоставляет все операторы, используемые в языке Perl. Более подробная информация по обращению к regex-функциям POSIX представлена в документации по языку PHP (см. раздел Ресурсы). В данной статье мы сосредоточим свое внимание на свойствах PCRE.
Регулярные выражения PHP PCRE содержат операторы, позволяющие путем сопоставления находить конкретные символы или другие операторы, определенные местоположения, например, начало и конец строки, начало или конец слова. Регулярные выражения также позволяют описывать альтернативы, которые можно задать альтернативы типа "или"-"или"; повторения фиксированной, изменяемой или неопределенной длины; наборы символов (например, "любая буква от a до m"); и классы, или типы символов (печатаемые символы, знаки препинания). Специальные операторы также разрешают использовать группировку — возможность применить оператор к целой группе других операторов.
В таблице 1 показаны некоторые типичные операторы регулярных выражений. Для создания сложных выражений можно последовательно объединять элементарные операторы из таблицы 1 (и другие).
Таблица 1. Типичные операторы регулярных выражений
Оператор Значение
. (точка) Любой одиночный символ
^ (крышка) Пустая последовательность в начале строки или цепочки
$ (знак доллара) Пустая последовательность в конце строки
A Буква A верхнего регистра
a Буква a нижнего регистра
\d Любая цифра
\D Любой нецифровой символ
\w Любая буква или цифра; синоним - [:alnum:]
[A-E] Любая заглавная буква из A, B, C, D или E
[^A-E] Любой символ, за исключением заглавных букв A, B, C, D или E
X? Найти совпадение по отсутствию или наличию одной заглавной буквы X
X* Ни одной или любое количество заглавных букв X
X+ Одна или несколько заглавных букв X
X{n} Ровно n заглавных букв X
X{n,m} Не менее n и не более m заглавных букв X; если опустить m, то выражение будет искать не менее n заглавных букв X
(abc|def)+ По меньшей мере одно вхождение последовательности abc и def
В следующем примере показано типичное использование регулярного выражения. Например, для web-сайта необходимо, чтобы каждый пользователь регистрировался. Имя пользователя должно начинаться с буквы и содержать от 3 до 10 буквенно-цифровых символов. Для проверки имени пользователя на соответствие ограничениям при отправке данных в приложение можно использовать следующее регулярное выражение: ^[A-Za-z][A-Za-z0-9_]{2,9}$.
Знак "крышка" соответствует началу строки. Первый набор [A-Za-z] соответствует любой букве. Второй набор [A-Za-z0-9_]{2,9} соответствует последовательности, содержащей от 2 до 9 букв, цифр или символов подчеркивания. Знак доллара ($) соответствует концу строки.
На первый взгляд, знак доллара может показаться лишним, однако его использование важно. Если его пропустить, то условиям данного регулярного выражения будет отвечать любая строка, которая начинается с буквы, содержит от 2 до 9 буквенно-цифровых символов и любое количество других символов. Иными словами, если бы не было знака доллара как привязки к концу строки, то подошла бы недопустимо длинная строка с подходящим началом, например, "martin1234-cruft" .
Программирование на языке PHP и регулярные выражения
В PHP есть функции для поиска совпадений в тексте, замены каждого совпадения на другой текст (похоже на операцию "найти и заменить") и поиска совпадений среди элементов списка. Вот эти функции:
Чтобы показать, как работают эти функции, давайте создадим небольшое PHP-приложение, которое будет просматривать список слов на соответствие определенному шаблону. Слова и регулярные выражения будут вводиться из обычной web-формы, а результаты отображаться в браузере посредством функции simple print_r(). Эта программка пригодится, если возникнет желание проверить или отладить регулярное выражение.
PHP-код показан в листинге 2. Все входные данные берутся из обычной HTML-формы. (Для краткости эту форму и PHP-код, отслеживающий ошибки, опустим.)
Листинг 2. Сравнение текста с шаблоном
Вначале с помощью функции preg_split() строка из слов, разделенных запятыми, преобразуется в отдельные элементы. Данная функция разбивает строку в тех местах, которые соответствуют условиям регулярного выражения. В данном случае регулярное выражение представляет собой просто "," , (запятая - разделитель списка слов, указанных через запятую). Слэш в начале и в конце просто показывает начало и конец regex.
Третий и четвертый аргументы функции preg_split() необязательны, но полезны. Добавьте в третий аргумент число n целого типа, если необходимо вернуть только первые n совпадений, или -1, если необходимо вернуть все совпадения. Если в качестве четвертого аргумента задать идентификатор PREG_SPLIT_NO_EMPTY, то функция preg_split() не будет возвращать пустые результаты.
Затем каждый элемент списка слов, разделенных запятыми, корректируется (убираются начальные и конечные пробелы) с помощью функции trim() и сравнивается с заданным регулярным выражением. Функция preg_grep() существенно упрощает процесс обработки списка: просто укажите в качестве первого аргумента шаблон, а в качестве второго - массив слов для сравнения. Функция возвращает массив совпадений.
Например, если в качестве шаблона задать регулярное выражение ^[A-Za-z][A-Za-z0-9_]{2,9}$ и список слов разной длины, то можно получить результат, показанный в листинге 3.
Листинг 3. Результат работы простого регулярного выражения
Кстати, с помощью дополнительного маркера PREG_GREP_INVERT можно инвертировать операцию preg_grep() и найти элементы, которые не совпадают с шаблоном (аналогично оператору grep -v в командной строке). Заменяя 22 строку на $matches = preg_grep( "/${_REQUEST[ 'regex' ]}/", $words, PREG_GREP_INVERT ) и используя входные данные из листинга 3, мы получим Array ( [1] => 1happy [2] => hermanmunster ).
Разбор строк
Функции preg_split() и preg_grep() очень удобны. Первая из них может разбирать строку на подстроки, если подстроки разделяются определенным шаблоном. Функция preg_grep() позволяет быстро отфильтровать список.
Но что произойдет, если строку нужно разобрать на составные части, используя одно или несколько сложных правил? Например, в США номера телефонов обычно выглядят следующим образом: "(305) 555-1212," "305-555-1212," или "305.555.1212." Если убрать пунктуацию, то количество символов сократится до 10 цифр, что легко можно определить с помощью регулярного выражения \d{10}. Однако код и префикс (каждый из которых состоит из трех цифр) телефонного номера США не могут начинаться с нуля или единицы (так как нуль и единица используются как префиксы для междугородных звонков). Вместо того чтобы разбивать числовую последовательность на отдельные цифры и создавать сложный код, для верификации можно использовать регулярное выражение.
Фрагмент кода позволяющий решить эту задачу, показан в листинге 4.
Листинг 4. Проверка американского телефонного номера
Давайте пройдем по этому коду:
* Как показано в таблице 1, в регулярных выражениях используется ограниченный набор специальных символов, например, квадратные скобки ([ ]) для наименования последовательности. Если надо найти такой символ в тексте, необходимо "выделить" специальный символ в регулярном выражении, поставив перед ним обратный слэш (\). Когда символ выделен, можно задать его посик, как и любого другого символа. Если нужно найти символ точки, например, в полном составном имени хоста, то напишите \.. При желании строку можно подать в функцию preg_quote() которая выполняет автоматическую изоляцию всех специальных символов регулярных выражений, как показано в строке 1. Если поставить echo() $punctuation после первой строки, то вы должны увидеть \(\)\.-.
* В строке 2 из телефонного номера убираются все знаки пунктуации. Функция preg_replace() заменяет все символы из $punctuation — операторы из набора [ ] - пустой строкой, эффективно устраняя такие символы. Возвращаемая новая строка присваивается переменной $number.
* В строке 4 определен шаблон верифицируемого телефонного номера США.
* Строка 5 реализует сопоставление, сравнивая телефонный номер, который теперь состоит только из цифр, с шаблоном. Функция preg_match() возвращает 1, если есть совпадение. Если совпадения нет, функция preg_match() возвращает нулевое значение. Если во время обработки возникла ошибка, то функция возвращает значение False (ложно). Таким образом, чтобы проверить удачное завершение, необходимо посмотреть, было ли возвращено значение 1. В противном случае проверьте итоговое значение функции preg_last_error() (если используется PHP версии 5.2.0 или выше). Если оно не равно нулю, то, возможно, был превышен лимит вычислений, например, разрешенная глубина рекурсии регулярного выражения. Обсуждение констант и ограничений, применяемых в регулярных выражениях PHP, представлено на странице, посвященной функциям регулярных выражений PCRE (см. раздел Ресурсы).
Извлечение данных
Во многих случаях необходимо только получить ответ на вопрос: "Соответствуют ли данные шаблону?" – например, при проверке данных. Однако чаще регулярные выражения используются для подтверждения соответствия и получения информации о совпадении.
Вернемся к примеру с телефонным номером. Пусть при соответствии шаблону нам необходимо сохранить код, префикс и номер линии в отдельных полях базы данных. Регулярные выражения могут запоминать совпадающие с шаблоном данные с помощью оператора capture. Оператор capture обозначается круглыми скобками и может использоваться в любой части регулярного выражения. Операции capture можно делать вложенными для поиска подсегментов в извлеченных сегментах данных. Например, чтобы из 10-значного номера телефона извлечь код города, префикс и номер линии, можно использовать следующую строку:
/([2-9][0-9]{2})([2-9][0-9]{2})([0-9]{4})/
Если входные данные соответствуют шаблону, первые три цифры захватываются первой парой круглых скобок, следующие три цифры - второй парой, а последние 4 цифры - последним оператором. Модификация вызова функции preg_match() возвращает извлеченные данные.
Листинг 5. Возврат извлеченных данных функцией preg_match()
Если в качестве третьего аргумента функции preg_match() указать переменную, например, в нашем коде, $matches, то в качестве ее значения будет выступать список извлеченных результатов. Нулевой элемент списка (с индексом 0) - это все совпадение целиком; первый элемент - совпадение, относящееся к первой паре круглых скобок, и так далее.
Вложенные операторы capture извлекают сегменты и подсегменты фактически любой глубины. Сложность с вложенными операторами capture состоит в том, чтобы определить, в какой части массива соответствий находится каждое соответствие, например, $matches. Действует следующее правило: подсчитайте порядковый номер открывающей скобки в регулярном выражении — этот номер и будет индексом нужного совпадения в массиве соответствий.
В листинге 6 показан пример (немного надуманный) извлечения частей городского адреса.
Листинг 6. Код для извлечения городского адреса
Опять все совпадение целиком хранится по индексу 0. А где хранится номер улицы? Если считать слева направо, номер улицы проверяется \d+. Это вторая открывающая круглая скобка слева, следовательно, значением $matches[2] будет 123. В $matches[4] оказывается название города, а в $matches[6] - почтовый индекс.
Продвинутые технологии
Обработка текста – широко распространенная задача, и PHP предоставляет ряд функций, упрощающих выполнение большого числа операций. Обратите внимание на следующее:
* Функция preg_replace() может работать как с одной строкой, так и с массивом строк. Если вызвать preg_replace() для массива строк, замена будет выполнена во всех элементах массива. В этом случае код preg_replace() возвращает массив измененных строк.
* Как и во всех остальных реализациях PCRE, здесь для осуществления замены можно прибегать к сравнению с вложенным шаблоном. Для наглядности давайте рассмотрим проблему стандартизации формата телефонного номера. Заменим все знаки пунктуации точками. Наше решение показано в листинге 7.
Листинг 7. Замена знаков пунктуации точками
Сопоставление с шаблоном и, в случае совпадения, перевод в стандартный телефонный номер выполняется за один шаг.
Откроем замечательную векторную программу Adobe Illustrator и попробуем нарисовать кусочек сыра (если вы не знаете, что такое векторный и подобные вещи, то читайте статью «Первые шаги - компьютерная графика и графические редакторы»).
Изучаем векторную графику (Adobe Illustrator).
Откроем замечательную векторную программу Adobe Illustrator и попробуем нарисовать кусочек сыра (если вы не знаете, что такое векторный и подобные вещи, то читайте статью «Первые шаги - компьютерная графика и графические редакторы»).
Для начала нужно создать новый документ (лист бумаги, на котором будем рисовать). В меню выбираем File - New...
Выбираем нужные размеры документа, и наживаем OK, чтобы его создать (в данном случае, взяты значения, те, что предложил Иллюстратор по умолчанию).
Так как рисуем сыр с нуля и еще не умеем, то воспользуемся сыром образцом (будем срисовывать).
Чтобы поместить не векторное изображение на лист, выбираем в меню: File - Place... - выбираем рисунок, который надо разместить на листе.
Прежде чем начать работать с сыром, его надо увеличить. Чтобы сделать саму картинку с сыром больше, надо кликнуть по ней левой кнопкой мыши - таким образом, выделится объект и вокруг него появится рамочка. Потянем за уголок - и таким образом можно изменить размер картинки. Чтобы при этом картинка не деформировалась и сохраняла пропорции, то придерживаем, когда тянем за уголок, клавишу Shift на клавиатуре.
Чтобы изменить видимый размер картинки (приблизить - отдалить) - выберем инструмент лупа (zoom tool) на основной панели инструментов. Этот инструмент, наводим на картинку, кликаем левой кнопкой мыши - изображение приблизится. Если наоборот, отдалить, то кликаем мышью по изображению, придерживая кнопку Alt на клавиатуре. Чтобы снова работать с изображением (т.е. выйти из режима лупы), надо выбрать другой инструмент на панели инструментов (например, следующий инструмент, с которым вы будете работать).
Итак, видимый размер куска сыра увеличен, чтобы было удобнее его обрисовывать, теперь фиксируем картинку с сыром на листе, чтобы, когда с ней начнем работать, она никуда не сдвинулась нечаянно. Для этого выбираем на панели инструментов черную стрелку (selecтion tool), после чего кликаем по рисунку, чтобы его выделить (всегда, объект, с которым работаем, должен быть выделен), затем в меню выбираем Object - Lock - Selecтion.
На панели инструментов выбираем инструмент перо (pen tool), с помощью него обрисовываем сыр. Но прежде чем, обрисовать сыр, надо убрать заливку объекта. Квадраты - белый и черный - внизу панели инструментов, указывают, какой цвет будет у объекта, который рисуем - он будет белым, и вокруг него будет черная граница.
Чтобы изменить цвет границы или цвет объекта - кликаем два раза по нужному квадрату (если нужно изменить границу - то по черному, в данном случае, если цвет объекта - по белому), и появится окошко, где можно выбрать другой, нужный цвет. Но в этом случае, другой цвет не нужен, надо, чтобы объект не был закрашен, и было видно только границу (контур объекта) - так будет удобнее срисовывать. Поэтому одним кликом мыши выбираем белый квадрат, и под ним выбираем иконку с квадратом, перечеркнутым красной полосой - таким образом, говорим иллюстратору - отмени заливку объекта белым цветом, и он это сделает.
Теперь начнем обрисовывать сыр, сначала светлую верхнюю плоскость, а затем нижнюю. Кликнем, допустим, в одном из уголков сыра, затем, повторяя контур сыра, кликаем дальше. Чтобы все линии (отрезки, которые появятся при этом), были прямыми, то когда надо кликнуть мышью, чтобы создать очередную точку, не отпуская левую клавишу мыши, тогда удерживая ее и двигая мышью, можно выгнуть отрезок, как угодно, и таким образом более точно обвести сыр.
Если был сделан отрезок кривым (выгнутым), то прежде чем продолжать обрисовывать объект, надо кликнуть левой кнопкой мыши по последней точке, которую поставили, и только затем создавать новую точку и отрезок. Если этого не сделаете, то следующий отрезок выгнется подобно предыдущему.
Если не верно поставлена точка, если отрезок как-то не так выгнут, то чтобы отменить действие, нажимаем на клавиатуре Ctrl+Z. Если отменили предыдущее действие ошибочно, то нажмите Ctrl+Shift+Z, чтобы вернуть все обратно.
Чтобы завершить обрисовывать верхнюю часть сыра надо обязательно закончить в той точке, с которой начали, чтобы получить цельный объект (т.е. замкнуть наш контур).
Аналогично первому создаем второй контур (объект).
Чтобы нагляднее было видно, что на картинке два контура: второй, недорисованный контур, немного сдвинут в сторону. При этом создание объекта не было прервано. Что делать, если надо довести контур объекта, после того как прервались? Чтобы продолжить работу, надо ткнуть кнопкой мышки в точку объекта, на которой прервались и можно дорисовывать объект дальше (естественно, при этом на панели инструментов должно быть выбрано перо, если надо дорисовать объект; если же надо подвигать объект, то переключаемся на selecтion tool - черная стрелка, только не забыть переключаться на нужные инструменты, прежде чем выполнять какие-то действия над объектом).
Когда нарисованы нужные объекты, следует их закрасить. Как это делается, объяснялось ранее: выделяем объект, и внизу панели через квадраты оперируем цветом. Есть и другие пути: можно вызвать цветовую палитру, чтобы она всегда на экране: Window - Color (опять же выделяем объект, прежде, чем дать ему цвет). Также можно, выделив объект, выбрать инструмент пипетка (eyedroррer tool) на панели инструментов, и ткнуть пипеткой по другому объекту на листе (например, по картинке с сыром, чтобы дать новому объекту такой же цвет, как цвет куска сыра, с которого срисовываем).
На данном примере три куска сыра: один - с какого срисовываем, второй - кусок сыра, которому дали такие же цвета, как сыру на картинке, но т.к. цвета не сырные, заплесневелые, то были подобранны другие цвета, которые можно видеть на третьем куске сыра. Если еще трудно сделать последнее самим, то сохраните картинку с примером на компьютере, поместите ее на лист бумаги в иллюстраторе и при помощи пипетки, дайте вашему куску сыра такие же яркие и приятные цвета, как у третьего куска сыра на картинке с примерами.
Как перемещать и копировать нарисованные объекты? Перемещать объекты надо следующим образом: выделить объект, предварительно выбрав инструмент selecтion tool (черная стрелка) на панели инструментов, а затем при помощи мышки перетащить выбранный объект. Если надо переместить сразу несколько объектов: опять же выбираем selecтion tool, и далее либо удерживая левую кнопку мыши выделяем все объекты, либо кликаем на нужные нам объекты поочередно, удерживая клавишу Shift на клавиатуре. После того как были выделены все нужные объекты, их можно перемещать, куда угодно. Также, выделив объекты, можно скопировать их Edit - Copy. Чтобы вставить скопированные объекты на лист - Edit - Paste. Чтобы удалить, выделенные объекты - Edit - Clear.
После придания куску, который рисуем, нужные цвета, рисуем на нем сырные дырки, чтобы придать ему еще большую схожесть с сыром. Дырки не будем срисовывать с фотографии куска сыра, а нарисуем сами. Дырка - это круг. Круги рисуют при помощи инструмента elliрse tool. Чтобы найти этот инструмент на панели инструментов надо кликнуть мышкой по инструменту квадрат (rectangle tool), удерживая мышку, выпадет меню, в котором надо выбрать elliрse tool. Далее кликаем на нашем листе в любом месте и рисуем круг.
Нарисовав круг, даем ему цвет более насыщенный и темный, чем боковая сторона сыра (т.к. дырка для этой стороны, а дырка она визуально более темная по цвету, чем сама сторона). Если переместить, нарисованный круг, на кусок сыра, то станет понятно, что он еще не смотрится дыркой, не хватает "глубины", так что надо приблизить круг (дырку) ближе к реальности, добавив ей бликов и теней.
Итак, скопируем круг, и вставим скопированное на лист (как это делается, рассказывалось ранее). Затем совместим круги так, как показано на рисунке (в итоге должно быть три круга - один исходный, и два которые, скопировав исходный круг, вставили на лист, а затем совместили между собой новые круги, как показано на рисунке):
Теперь порежем эти круги при помощи Pathfinder. Вызываем его через меню: Window - Pathfinder. Далее оба круга выделяем, и нажимаем в окошке Pathfinder инструмент Divide. Если сейчас попытаться подвигать круги, то обнаружите, что они слиплись в один объект. На самом деле, Divider порезал наши круги на много маленьких объектов, но для удобства, он их потом сгруппировал (вдруг захочется их передвинуть сразу куда-нибудь). Поэтому надо рассгруппировать все объекты. Выделяем нашу группу объектов, выбираем в меню - Object - Ungroup.
Если теперь попробовать мышкой подвигать круги, то видим, что их все-таки разрезали, и получили в итоге три очень интересных объекта: два полумесяца и объект похожий на дыню. Круги и резали, т.к. нужны были полумесяцы. Скопируем один из полумесяцев, вставим на лист, дадим ему цвет чуть бледнее, чем цвет круга, и переместим на круг, как показано на нашем рисунке (см. чуть выше).
Затем еще раз копируем полумесяц и вставляем его на лист, делаем его еще светлее, и переносим на наш круг, как показано на рисунке:
Теперь нужен другой полумесяц, зеркальный тем, что копировали до этого. Берем полумесяц, копируем, даем ему самый темный цвет по сравнению с другими объектами, перемещаем на круг так, как показано на рисунке:
Вот и все, сырная дырка готова, теперь надо переместим ее на сыр. Для этого надо сгруппировать все объекты, имеющие отношение к сырной дырке, чтобы потом было легче их копировать и перемещать при необходимости. Чтобы сгруппировать объекты, их нужно выделить, а затем группируем их, выбрав в меню Object - Group (если надо будет снова разбить объекты, отклеить друг от друга, то выделяем сгруппированные объекты, и при помощи уже знакомого Object - Ungroup).
Итак, переместили дырку на сыр, теперь скопируем ее, и вставим еще несколько новых дырок на боковую сторону сыра. Сделаем их размеры разными (как, уменьшать-увеличивать размер объекта говорили в начале статьи). В итоге, должны получить, вот такой сыр:
Чтобы сыр был похож на настоящий, добавим еще одну полудырку, а так же теней и рефлексов на края.
Для этого берем готовую дырку, копируем, вставляем на лист (отдельно от сыра), и затем при помощи Pathfinder и Divider ее ополовиниваем. Также копируем два полумесяца, которые потом, вставим на край сыра. Получаются две следующие картинки:
Теперь и половинку дырки, и два полумесяца немного развернем при помощи инструмента rotate tool. Края сыра идут немного под наклоном, и чтобы совместить дырки с краями куска сыра, их надо привести в соответствие. Теперь наложим их на кусок сыра, если надо еще чуть-чуть поразворачиваем, и в итоге должны получить следующее:
Итак, кусок сыра почти нарисовали, теперь надо нарисовать дырки для верхней плоскости (копируем уже имеющиеся дырки, и немного изменяем им цвет на посветлее, затем немного их трансформируем до овалов, и разворачиваем при помощи rotate tool):
Теперь еще для красоты можно нарисовать при помощи пера парочку рефлексов и теней, и сыр готов:
Можете идти ловить мышей. Хотя... умея рисовать картинки в Иллюстраторе, вам будет не до мышей.
Напомним, что IP относится к группе протоколов TCP/IP. Протокол TCP реализует транспортные функции модели OSI (Open Systems Interconnection), ее четвертого уровня. Его основная обязанность - обеспечение надежной связи между начальной и конечной точками пересылки данных. IP располагается в OSI на сетевом, или третьем, уровне; он должен поддерживать передачу маршрутизаторам адресов отправителя и получателя каждого пакета на всем пути его следования.
Маршрутизаторы и коммутаторы третьего уровня считывают записанную в пакетах по правилам IP и других протоколов третьего уровня информацию и используют ее совместно с таблицами маршрутизации и некоторыми другими интеллектуальными средствами поддержки работы сети, пересылая данные по сетям TCP/IP любого масштаба - от "комнатной" до глобальной, охватывающей всю планету.
Процесс маршрутизации начинается с определения IP-адреса, уникального для станции-отправителя (адреса источника), который может быть постоянным или динамическим. Каждый пакет содержит такой адрес, длина которого, в соответствии с современной спецификацией IPv4, составляет 32 бита.
Кроме того, в заголовке пакета записан IP-адрес его места назначения. Если отправляющая станция определяет, что адрес доставки не локальный, пакет направляется маршрутизатору первого сетевого сегмента. Этот маршрутизатор определяет IP-адрес пакета и проверяет по своей таблице, не расположена ли станция получателя в локальной физически подключенной к нему сети, которая называется IP-подсетью (обычно она назначается для всех сетевых интерфейсов маршрутизатора). Если же выясняется, что IP-адрес получателя локальный, маршрутизатор начинает искать внутреннее хранилище IP- и MAC-адресов локальных устройств - ARP-кэш (Adress Resolution Protocol), позволяющий сопоставлять IP- и MAC-адреса.
При обнаружении нужного MAC-адреса маршрутизатор помещает его в заголовок пакета (удаляя собственный MAC-адрес, который больше не нужен) и направляет пакет по месту назначения. Если MAC-адрес получателя не найден в ARP-кэше, маршрутизатор пересылает ARP-запрос в подсеть, соответствующую IP-адресу получателя пакета, где конечная станция с этим IP-адресом передает ответ на запрос, содержащий необходимый MAC-адрес. Затем маршрутизатор обновляет содержимое кэша, устанавливает новый MAC-адрес в заголовке пакета и отправляет его. Если пакет не предназначен для локальной подсети, маршрутизатор направляет его на маршрутизатор следующего сегмента по MAC-адресу последнего.
Процесс построения и обновления таблиц маршрутизации практически непрерывен. Он осуществляется средствами, использующими интеллектуальные протоколы обнаружения, например RIP или OSPF. В таблице каждого маршрутизатора указан оптимальный маршрут до адреса назначения или до маршрутизатора следующего сегмента (если адрес не принадлежит локальной подсети). Последовательно просматривая собственные таблицы маршрутизации, соответствующие устройства передают пакет "по этапу", запрашивая, при необходимости, MAC-адрес конечной станции. Этот процесс продолжается до тех пор, пока пакет не доберется до пункта назначения.
Однако при пересылке пакета через множество сетевых сегментов существует опасность образования "петель": неправильно сконфигурированный маршрутизатор постоянно возвращает пакет тому маршрутизатору, через который данный пакет уже проходил. Во избежание этого в IP предусмотрена TTL-функция (time-to-live), позволяющая задать предел времени путешествия пакета по сети. Значение TTL устанавливается заранее и уменьшается на единицу при каждом прохождении любого сегмента. Если величина TTL становится равной нулю, пакет удаляется, а маршрутизатор отсылает отправителю сообщение ICMP.
Механизм IP- маршрутизации
1. Маршрутизатор проверяет IP-адрес входящего пакета и просматривает т аблицу, определяя, не является ли пунктом назначения локальная сеть.
2. Если IP-адрес назначения локальный, то маршрутизатор находит во внутреннем хранилище IP- и MAC-адресов локальных устройств MAC-адрес места назначения, помещает его в заголовок пакета и направляет пакет получателю.
3. Если MAC-адрес получателя не обнаруживается, маршрутизатор должен послать запрос о нем по IP-адресу получателя. Если после просмотра таблицы выясняется, что пакет не предназначен для локальной сети, маршрутизатор переправляет его маршрутизатору следующего сетевого сегмента, используя MAC-адрес последнего.