Самоучитель по PHP 4
.pdf
Глава 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) не поддерживают любое количе-
