Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель по PHP 4

.pdf
Скачиваний:
86
Добавлен:
02.05.2014
Размер:
4.36 Mб
Скачать

Глава 22. Основы регулярных выражений в формате RegEx

311

Выделение всех уникальных слов из текста

Задача: перед нами некоторый довольно длинный текст в переменной $text. Необходимо выделить из него все слова и оставить из них только уникальные. Результат должен быть представлен в виде списка, отсортированного в алфавитном порядке. Решение этой задачи может потребоваться, например, при написании индексирующей поисковой системы на PHP.

Решение: воспользуемся функцией split() и ассоциативным массивом.

Листинг 22.1. Отбор уникальных слов

//Эта функция выделяет из текста в $text все уникальные слова и

//возвращает их список, отсортированный в алфавитном порядке. function GetUniques($text)

{ // Сначала получаем все слова в тексте $Words=split("[[:punct:][:blank:]]+",$text); $Uniq=array(); // список уникальных слов $Test=array(); // хэш уже обработанных слов

//Проходимся по всем словам в $Words и заносим в $Uniq уникальные foreach($Words as $v) {

$v=strtolower($v); // в нижний регистр

//Слово уже нам встречалось? Если нет, то занести в $Uniq if(!@$Test[$v]) $Uniq[]=$v;

//Указать, что это слово уже обрабатывалось

$Test[$v]=1;

}

// Наконец, сортируем список sort($Uniq);

return $Uniq;

}

Данный пример довольно интересен, т. к. он имеет довольно большую функциональность при небольшом объеме. Его "сердце" — функция split() и цикл перебора слов с отбором уникальных. Мы используем алгоритм, основанный на применении ассоциативного массива для отбора уникальных элементов. Как он работает — надеюсь, ясно из комментариев.

Теперь мы можем воспользоваться функцией из листинга 22.1, например, в таком контексте:

$fname="sometext.txt";

312

Часть IV. Стандартные функции PHP

$f=fopen($fname,"r"); $text=fread($f,filesize($fname)); fclose($f); $Uniq=GetUniques($text); foreach($Uniq as $v) echo "$v ";

Интересно будет отметить, что функция preg_split(), которая работает с регулярными выражениями в формате PCRE, и которую мы не рассматриваем в этой книге, показывает гораздо лучшую производительность в этом примере, чем split() чуть ли не в 3 раза быстрее! Если вам нужна максимальная производительность, пожалуй, будет лучше воспользоваться именно ей, но прежде почитайте что-нибудь о Perl и его регулярных выражениях напри- мер, в замечательной книге Perl Cookbook Тома Кристиансена и Ната Торкинг- тона (русское издание: "Библиотека программиста. Perl", издательство Питер, 2000).

Заключение

Конечно, можно придумать и еще множество примеров применения регулярных выражений. Вы наверняка сможете это сделать самостоятельно — особенно после некоторой практики, которая так важна для понимания этого материала.

Однако я хочу обратить ваше внимание на то, что во многих задачах как раз не обязательно применять регулярные выражения. Так, например, задачи "поставить слэш перед всеми кавычками в строке" и "заменить в строке все кавычки на "" можно и нужно решать при помощи str_replace(), а не ereg_Replace() (это существенно — раз в 20 — повысит быстродействие). Не забывайте, что регулярное выражение — некоторого рода "насилие" над компьютером, принуждение делать нечто такое, для чего он мало приспособлен. Этим объясняется медлительность механизмов обработки регулярных выражений, экспоненциально возрастающая с ростом сложности шаблона.

Глава 23

Работа с изображениями

Как мы знаем, одним из самых важных достижений WWW по сравнению со всеми остальными службами Интернета стала возможность представления в браузерах пользователей мультимедиа-информации, а не только "сухого" текста. Основной объем этой информации приходится, конечно же, на изображения.

Разумеется, было бы довольно расточительно хранить и передавать все рисунки в обыкновенном растровом формате (наподобие BMP), тем более, что современные алгоритмы сжатия позволяют упаковывать такого рода данные в сотни и более раз эффективней. Чаще всего для хранения изображений используются три формата сжатия с перечисленными ниже свойствами.

rJPEG. Идеален для фотографий, но сжатие изображения происходит с потерями качества, так что этот формат совершенно не подходит для хранения различных диаграмм и графиков.

rGIF. Позволяет достичь довольно хорошего соотношения размер/качество, в то же время не искажая изображение; применяется в основном для хранения небольших точечных рисунков и диаграмм.

rPNG. Сочетает в себе хорошие стороны как JPEG, так и GIF, но в настоящий момент ему почему-то не выражают особого доверия — скорее, по историческим причинам, из-за нежелания отказываться от GIF и т. д.

В последнее время GIF все более вытесняется форматом PNG, что связано в первую очередь с окончанием действия бесплатной лицензии изобретателя на его использование. К сожалению, для небольших изображений GIF все еще остается самым оптимальным форматом, оставляя позади (иногда далеко позади) PNG.

Зачем может понадобиться в Web-программировании работа с изображениями? Разве это не работа дизайнера?

В большинстве случаев это действительно так. Однако есть и исключения, например, графические счетчики (автоматически создаваемые картинки с отображаемым поверх числом, которое увеличивается при каждом "заходе" пользователя на страницу), или же графики, которые пользователь может строить в реальном времени — скажем, диаграммы сбыта продукции или снижения цен на комплектующие. Все эти приложения требуют как минимум умения генерировать изображения "на лету", причем с довольно большой скоростью. Чтобы этого добиться на PHP, можно применить два способа: задействовать какую-нибудь внешнюю утилиту для формирования изобра-

Глава 23. Работа с изображениями

315

жения (например, известную программу fly), или же воспользоваться встроенными функциями PHP для работы с графикой. Оба способа имеют как достоинства, так и недостатки, но, пожалуй, недостатков меньше у второго метода, так что им-то мы и займемся в этой главе.

С недавнего времени все программные продукты, которые умели формировать изображения в формате GIF, переориентируются на PNG. В частности, не так давно компания, поддерживающая библиотеку GD для работы с GIF-изображениями, переписала ее код с учетом формата PNG. Так как PHP использует эту библиотеку, то поддержка GIF автоматически исключилась и из него. К счастью, в Интернете все еще можно найти старые версии GD с поддержкой GIF и, таким образом, настроить PHP для работы с этим форматом, но задумайтесь: стоит ли теперь применять GIF, если весь мир вполне успешно переходит на PNG, тем более, что его поддерживают практически все современные браузеры (четвертой версии) — а это 98% от используемого их числа...

Универсальная функция

GetImageSize()

Что же, работать с картинками приходится часто — гораздо чаще, чем может показаться на первый взгляд. Среди наиболее распространенных операций можно особо выделить одну — определение размера рисунка. Чтобы сделать программистам "жизнь раем", разработчики PHP встроили в него функцию, которая работает практически со всеми распространенными форматами изображений, в том числе с GIF, JPEG и PNG.

list GetIimageSize(string $filename [,array& $imageinfo])

Эта функция предназначена для быстрого определения в сценарии размеров (в пикселах) и формата рисунка, имя файла которого передано ей в первом параметре. Она возвращает список из четырех элементов. Первый элемент (с ключом 0) хранит ширину картинки в пикселах, второй (с ключом 1) — его высоту. Ячейка массива с ключом 2 определяется форматом изображения: 0, если это GIF, 1 в случае JPG и 2 для PNG. Следующий элемент, имеющий ключ 3, будет содержать после вызова функции строку примерно следующего вида: height=sx width=sy, где sx и sy — соответственно ширина и высота изображения. Это применение задумывалось для того, чтобы облегчить вставку данных о размере изображения в тэг <img>, который может быть сгенерирован сценарием.

316

Часть IV. Стандартные функции PHP

Работа с изображениями и библиотека GD

Давайте теперь рассмотрим идею создания рисунков сценарием "на лету". Например, как мы уже замечали, это очень может пригодиться при создании сценариевсчетчиков, графиков, картинок-заголовков да и многого другого.

Для деятельности такого рода существует специальная библиотека под названием GD. Она содержит в себе множество функций (такие как рисование линий, растяжение/сжатие изображения, заливка до границы, вывод текста и т. д.), которые могут использовать программы, поддерживающие работу с данной библиотекой. PHP (со включенной поддержкой GD) как раз и является такой программой.

Поддержка GD включается при компиляции и установке PHP. Возможно, неко- торые хостинг-провайдеры ее не имеют. Выясните, работает ли PHP вашего хостера с библиотекой GD.

Пример

Начнем сразу с примера сценария, который представляет собой не HTML-страницу в обычном смысле, а рисунок PNG. То есть URL этого сценария можно поместить в тэг:

<img src=button.php?Hello+world!>

Как только будет загружена страница, содержащая указанный тэг, сценарий запустится и отобразит надпись Hello world! на фоне рисунка, лежащего в images/button.png. Полученная картинка нигде не будет храниться — она создается "на лету".

Рис. 23.1. Демонстрация возможностей вывода

TrueType-шрифтов на PHP

Глава 23. Работа с изображениями

317

Листинг 23.1. Создание картинки "на лету"

<?

//Получаем строку, которую нам передали в параметрах $string=$QUERY_STRING;

//Загружаем рисунок фона с диска

$im = imageCreateFromPng("images/button.png"); // Создаем в палитре новый цвет — оранжевый $orange = imageColorAllocate($im, 220, 210, 60);

//Вычисляем размеры текста, который будет выведен $px = (imageSx($im)-7.5*strlen($string))/2;

//Выводим строку поверх того, что было в загруженном изображении imageString($im,3,$px,9,$string,$orange);

//Сообщаем о том, что далее следует рисунок PNG

Header("Content-type: image/png");

//Теперь — самое главное: отправляем данные картинки в

//стандартный выходной поток, т. е. в браузер imagePng($im);

//В конце освобождаем память, занятую картинкой imageDestroy($im);

?>

Итак, мы получили возможность "на лету" создавать стандартные кнопки с разными надписями, имея только "шаблон" кнопки.

Создание изображения

Давайте теперь разбираться, как работать с картинками в GD. Для начала нужно картинку создать — пустую (при помощи imageCreate()) или же загруженную с диска

(imageCreateFromPng(), imageCreateFromJpeg() или imageCreateFromGif(), в зависимости от того, какие форматы поддерживаются

PHP и GD).

int imageCreate(int $x, int $y)

Создает пустую картинку размером $x на $y точек и возвращает ее идентификатор. После того, как картинка создана, вся работа с ней осуществляется именно через этот идентификатор, по аналогии с тем, как мы работаем с файлом через его дескриптор.

int imageCreateGromPng(string $filename) или int imageCreateGromJpeg(string $filename) или int imageCreateGromif(string $filename)

318

Часть IV. Стандартные функции PHP

Эти функции загружают изображения из файла в память и возвращают его идентификатор. Как и после вызова imageCreate(), дальнейшая работа с картинкой возможна только через этот идентификатор. При загрузке с диска изображение распаковывается и хранится в памяти уже в неупакованном формате, для того чтобы можно было максимально быстро производить с ним различные операции, такие как масштабирование, рисование линий и т. д. При сохранении на диск или выводе в браузер функцией imagePng() (или, соответственно, imageJpeg() и imageGif()) картинка автоматически упаковывается.

Стоит заметить, что не обязательно все три функции будут доступны в вашей версии PHP. Скорее всего, в ней не окажется функции imageCreateFromGif(), а возможно, и imageCreateFromJpeg(), потому что от первой разработчики GD просто отказались, а вторая появилась сравнительно недавно. Надеюсь, в скором времени ситуация нормализуется, но сейчас, к сожалению, это не так.

Определение параметров изображения

Как только картинка создана и получен ее идентификатор, GD становится совершенно все равно, какой формат она имеет и каким путем ее создали. То есть все остальные действия с картинкой происходят через ее идентификатор, вне зависимости от формата, и это логично — ведь в памяти изображение все равно хранится в распакованном виде (наподобие BMP), а значит, информация о ее формате нигде не используется. Так что вполне возможно открыть PNG-изображение с помощью imageCreateFromPng() и сохранить ее на диск функцией imageJpeg(), уже в другом формате. В дальнейшем можно в любой момент времени определить размер загруженной картинки, воспользовавшись функциями imageSX() и imageSY():

int imageSX(int $im)

Функция возвращает горизонтальный размер изображения, заданного своим идентификатором, в пикселах.

int imageSY(int $im)

Возвращает высоту картинки в пикселах.

int imageColorsTotal(int $im)

Эту функцию имеет смысл применять только в том случае, если вы работаете с изображениями, "привязанными" к конкретной палитре — например, с файлами GIF. Она возвращает текущее количество цветов в палитре. Как мы вскоре увидим, каждый вызов imageColorAllocate() увеличивает размер палитры. В то же время известно, что если при небольшом размере палитры GIF-картинка сжимается очень хорошо, то при переходе через степень двойки (например, от 16 к 17 цветам) эффективность сжатия заметно падает, что ведет к увеличению размера (так уж устроен

Глава 23. Работа с изображениями

319

формат GIF). Если мы не хотим этого допустить и собираемся вызывать imageColorAllocate() только до предела 16 цветов, а затем перейти на использование imageColorClosest(), нам очень может пригодиться рассматриваемая функция.

Сохранение изображения

Давайте займемся функцией, поставленной в листинге 23.1 "на предпоследнее место", которая, собственно, и выполняет большую часть работы — выводит изображение в браузер пользователя. Оказывается, эту же функцию можно применять и для сохранения рисунка в файл.

int imagePng(int $im [,string $filename]) или int imageJpeg(int $im [,string $filename]) или int imageGif(int $im [,string $filename])

Эти функции сохраняют изображение, заданное своим идентификатором и находящееся в памяти, на диск, или же выводят его в браузер. Разумеется, вначале изображение должно быть загружено или создано при помощи функции imageCreate(), т. е. мы должны знать его идентификатор $im.

Если аргумент $filename опущен, то сжатые данные в соответствующем формате выводятся прямо в стандартный выходной поток, т. е. в браузер. Нужный заголовок Content-type при этом не выводится, ввиду чего нужно выводить его вручную при помощи Header(), как это было показано в примере из листинга 23.1.

Некоторые браузеры не требуют вывода правильного Content-type, а опре- деляют, что перед ними рисунок, по нескольким первым байтам присланных данных. Ни в коем случае не полагайтесь на это! Дело в том, что все еще су- ществуют браузеры, которые этого делать не умеют. Кроме того, такая техника идет вразрез со стандартами HTTP.

Фактически, вы должны вызвать одну из трех команд, в зависимости от типа изображения:

Header("Content-type: image/png") для PNG

Header("Content-type: image/jpeg") для JPEG

Header("Content-type: image/gif") для GIF

Рекомендую их вызывать не в начале сценария, а непосредственно перед вызовом imagePng(), imageGif() или imageJpeg(), поскольку иначе вы не сможете никак увидеть сообщения об ошибках и предупреждения, которые, возможно, будут сгенерированы программой.

320

 

Часть IV. Стандартные функции PHP

 

 

 

 

 

 

К рассмотренным только что функциям можно сделать точно такие же замеча- ния, как и к семейству imageCreateFromXXX(), т. е., некоторые из них могут отсутствовать скорее всего, последняя. Однако случаются и забавные казу- сы. Я видел версию PHP, в которой не поддерживалась вообще ни одна из этих функций, ровно как и функции imageCreateFromXXX(). В то же время imageCreate() работала (во всяком случае, так казалось). Возникает инте- ресный вопрос: мы можем создавать изображения, рисовать в них линии, кру- ги, выводить текст, но не в состоянии ни сохранить их где-нибудь, ни даже за- грузить уже готовую картинку с диска. Зачем тогда вообще были нужны все остальные функции?..

Работа с цветом в формате RGB

Наверное, теперь вам захочется что-нибудь нарисовать на пустой или только что загруженной картинке. Но чтобы рисовать, нужно определиться, каким цветом это делать. Проще всего указать цвет заданием тройки RGB-значений (от red-green-blue) — это три цифры от 0 до 255, определяющие содержание красной, зеленой и синей составляющих в нужном нам цвете. Число 0 обозначает нулевую яркость соответствующей компоненты, а 255 — максимальную интенсивность. Например, (0,0,0) зада-

ет черный цвет, (255,255,255) — белый, (255,0,0) — ярко-красный, (255,255,0) —

желтый и т. д.

Правда, GD не умеет работать с такими тройками напрямую. Она требует, чтобы перед использованием RGB-цвета был получен его специальный идентификатор. Дальше вся работа опять же осуществляется через этот идентификатор. Скоро станет ясно, зачем нужна такая техника.

Создание нового цвета

int imageColorAllocate(int $im, int $red, int $green, int $blue)

Функция возвращает идентификатор цвета, связанного с соответствующей тройкой RGB. Обратите внимание, что первым параметром функция требует идентификатор изображения, загруженного в память или созданного до этого. Практически каждый цвет, который планируется в дальнейшем использовать, должен быть получен (определен) при помощи вызова этой функции. Почему "практически" — станет ясно после рассмотрения функции imageColorClosest().

Получение ближайшего цвета

Давайте разберемся, зачем это придумана такая технология работы с цветами через промежуточное звено — идентификатор цвета. А дело все в том, что некоторые форматы изображений (такие как GIF и частично PNG) не поддерживают любое количе-