Глава 8. Строки и регулярные выражения в PHP

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

Поиск по шаблону позволяет не только находить определенные фрагменты текста, но и заменять их другими фрагментами. Одним из стандартных примеров поиска по шаблону являются команды поиска/замены в текстовых редакторах — например, в MS Word, Emacs и в моем любимом редакторе vi. Всем пользователям UNIX хорошо известны такие программы, как sed, awk и grep; богатство возможностей этих программ в значительной степени обусловлено средствами поиска по шаблону. Механизмы поиска по шаблону решают четыре основные задачи:

Появление Web породило необходимость в более быстрых и эффективных средствах поиска данных, которые бы позволяли пользователям со всего мира находить нужную информацию среди миллиардов web-страниц. Поисковые системы, онлайновые финансовые службы и сайты электронной коммерции — все это стало бы абсолютно бесполезным без средств анализа гигантских объемов данных в этих секторах. Действительно, средства обработки строковой информации являются жизненно важной составляющей практически любого сектора, так или иначе связанного с современными информационными технологиями. В этой главе основное внимание посвящено средствам обработки строк в РНР. Мы рассмотрим некоторые стандартные строковые функции (в языке их больше 60!), а из приведенных определений и примеров вы получите сведения, необходимые для создания web-приложений. Но прежде чем переходить к специфике РНР, я хочу познакомить вас с базовым механизмом, благодаря которому становится возможным поиск по шаблону. Речь идет о регулярных выражениях.

Регулярные выражения

Регулярные выражения лежат в основе всех современных технологий поиска по шаблону. Регулярное выражение представляет собой последовательность простых и служебных символов, описывающих искомый текст. Иногда регулярные выражения бывают простыми и понятными (например, слово dog), но часто в них присутствуют служебные символы, обладающие особым смыслом в синтаксисе регулярных выражений, — например, <(?)>.*<\/.?>.

В РНР существуют два семейства функций, каждое из которых относится к определенному типу регулярных выражений: в стиле POSIX или в стиле Perl. Каждый тип регулярных выражений обладает собственным синтаксисом и рассматривается в соответствующей части главы. На эту тему были написаны многочисленные учебники, которые можно найти как в Web, так и в книжных магазинах. Поэтому я приведу лишь основные сведения о каждом типе, а дальнейшую информацию при желании вы сможете найти самостоятельно. Если вы еще не знакомы с принципами работы регулярных выражений, обязательно прочитайте краткий вводный курс, занимающий всю оставшуюся часть этого раздела. А если вы хорошо разбираетесь в этой области, смело переходите к следующему разделу.

Синтаксис регулярных выражений (POSIX)

Структура регулярных выражений POSIX чем-то напоминает структуру типичных математических выражений — различные элементы (операторы) объединяются друг с другом и образуют более сложные выражения. Однако именно смысл объединения элементов делает регулярные выражения таким мощным и выразительным средством. Возможности не ограничиваются поиском литерального текста (например, конкретного слова или числа); вы можете провести поиск строк с разной семантикой, но похожим синтаксисом — например, всех тегов HTML в файле.

Простейшее регулярное выражение совпадает с одним литеральным символом — например, выражение g совпадает в таких строках, как g, haggle и bag. Выражение, полученное при объединении нескольких литеральных символов, совпадает по тем же правилам — например, последовательность gan совпадает в любой строке, содержащей эти символы (например, gang, organize или Reagan).

Оператор | (вертикальная черта) проверяет совпадение одной из нескольких альтернатив. Например, регулярное выражение php | zend проверяет строку на наличие php или zend.

Квадратные скобки

Квадратные скобки ([ ]) имеют особый смысл в контексте регулярных выражений — они означают «любой символ из перечисленных в скобках». В отличие от регулярного выражения php, которое совпадает во всех строках, содержащих литеральный текст php, выражение [php] совпадает в любой строке, содержащей символы р или h. Квадратные скобки играют важную роль при работе с регулярными выражениями, поскольку в процессе поиска часто возникает задача поиска символов из заданного интервала. Ниже перечислены некоторые часто используемые интервалы:

Конечно, перечисленные выше интервалы всего лишь демонстрируют общий принцип. Например, вы можете воспользоваться интервалом [0-3] для обозначения любой десятичной цифры от 0 до 3 или интервалом [b-v] для обозначения любого символа нижнего регистра от b до v. Короче говоря, интервалы определяются совершенно произвольно.

Квантификаторы

Существует особый класс служебных символов, обозначающих количество повторений отдельного символа или конструкции, заключенной в квадратные скобки. Эти служебные символы (+, * и {...}) называются квантификаторами. Принцип их действия проще всего пояснить на примерах:

Прочие служебные символы

Служебные символы $ и ^ совпадают не с символами, а с определенными позициями в строке. Например, выражение р$ означает строку, которая завершается символом р, а выражение ^р — строку, начинающуюся с символа р.

Объединение служебных символов приводит к появлению более сложных выражений. Рассмотрим несколько примеров:

Иногда требуется найти служебные символы в строках вместо того, чтобы использовать их в описанном специальном контексте. Для этого служебные символы экранируются обратной косой чертой (\). Например, для поиска денежной суммы в долларах можно воспользоваться выражением \$[0-9]+, то есть «знак доллара, за которым следует одна или несколько десятичных цифр». Обратите внимание на обратную косую черту перед $. Возможными совпадениями для этого регулярного выражения являются $42, $560 и $3.

Стандартные интервальные выражения (символьные классы)

Для удобства программирования в стандарте POSIX были определены некоторые стандартные интервальные выражения, также называемые символьными классами (character classes). Символьный класс определяет один символ из заданного интервала — например, букву алфавита или цифру:

Функции РНР для работы с регулярными выражениями (POSIX-совместимые)

В настоящее время РНР поддерживает семь функций поиска с использованием регулярных выражений в стиле POSIX:

Описания этих функций приведены в следующих разделах.

ereg()

Функция ereg() ищет в заданной строке совпадение для шаблона. Если совпадение найдено, возвращается TRUE, в противном случае возвращается FALSE. Синтаксис функции ereg():

int ereg(string шаблон, string строка [, array совпадения]);

Поиск производится с учетом регистра алфавитных символов. Пример использования ereg() для поиска в строках доменов .com:

$is_com = ereg("(\.)(com$)", $email);

Функция возвращает TRUE, если $email завершается символами ".com". В частности, поиск будет успешным для строк "www.wjgilmore.com" и "someemail@apress.com"

Обратите внимание: из-за присутствия служебного символа $ регулярное выражение совпадает только в том случае, если строка завершается символами .com. Например, оно совпадет в строке "www.apress.com", но не совпадет в строке "www.apress.com/catalog".

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

Листинг 8.1. Вывод элементов массива $regs

$url = "http://www.apress.com";
// Разделить $url на три компонента: "http://www". "apress" и "com"
$www_url = ereg("^(http://www)\.([[:alnum:]+\.([[:alnum:]]+)". $url, $regs);
if ($www_url) : // Если переменная $www_url содержит URL
echo $regs[0]; // Вся строка "http://www.apress.com"
print "<br>";
echo $regs[l]; // "http://www"
print "<br>";
echo $regs[2]; // "apress"
print "<br>";
echo $regs[3]; // "com" endif;

При выполнении сценария в листинге 8.1 будет получен следующий результат:

http://www.apress.com http://www apress com

Примечание:
Функция `ereg()` считается устаревшей в последних версиях PHP и её применение не рекомендуется. Для работы с регулярными выражениями в современных приложениях лучше использовать функции на основе PCRE, например, `preg_match()`. При создании новых проектов предпочтение следует отдавать актуальным методам обработки регулярных выражений для обеспечения надежности и защиты вашего кода.

ereg_replace()

Функция ereg_replace() ищет в заданной строке совпадение для шаблона и заменяет его новым фрагментом. Синтаксис функции ereg_replace():

string ereg_replace (string шаблон, string замена, string строка)

Функция ereg_replace() работает по тому же принципу, что и ereg(), но ее возможности расширены от простого поиска до поиска с заменой. После выполнения замены функция возвращает модифицированную строку. Если совпадения отсутствуют, строка остается в прежнем состоянии. Функция ereg_replace(), как и ereg(), учитывает регистр символов. Ниже приведен простой пример, демонстрирующий применение этой функции:

$copy_date = "Copyright 1999";
$copy_date = ereg_replace("([0-9]+)", "2000", $copy_date);
print $copy_date; // Выводится строка "Copyright 2000"

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

$url = "Apress (http://www.apress.com)";
$url = ereg_replace("http://(([A-Za-z0-9.-]+))", "<a href=\"\\0\">\\0</a>", $url);
print $url;

Результат выполнения:

Apress (<a href="http://www.apress.com">http://www.apress.com</a>)

Примечание:
Функция ereg_replace() и вся серия функций ereg устарели начиная с PHP 5.3.0 и были полностью удалены в PHP 7.0.0. Используйте функции на основе PCRE (Perl-совместимые регулярные выражения), такие как preg_replace(), для выполнения похожих операций в современных версиях PHP.

eregi()

Функция eregi() выполняет поиск совпадений для заданного шаблона в строке. Синтаксис функции eregi() следующий:

int eregi(string шаблон, string строка [, array совпадения]);

Эта функция выполняет поиск без учета регистра символов. Функция eregi() особенно полезна при проверке корректности вводимых данных, таких как пароли. Пример использования функции eregi():

$password = "abc";
if (!eregi("[[:alnum:]]{8,10}", $password)):
    print "Invalid password! Passwords must be from 8 to 10 characters in length.";
endif;

В результате выполнения этого кода будет выведено сообщение об ошибке, так как длина пароля "abc" не соответствует требуемому интервалу от 8 до 10 символов.

Примечание:
Функция `eregi()` считается устаревшей в последних версиях PHP, и её использование не рекомендуется. Для работы с регулярными выражениями в современных приложениях рекомендуется использовать функции на основе PCRE, например, `preg_match()`. Это поможет обеспечить надежность и безопасность вашего кода.

eregi_replace()

Функция eregi_replace() аналогична функции ereg_replace(), но поиск выполняется без учета регистра символов. Синтаксис функции eregi_replace() следующий:

string eregi_replace(string шаблон, string замена, string строка);

Примечание:
Функция `eregi_replace()` устарела в новых версиях PHP, и её использование не рекомендуется. Вместо нее лучше использовать функции, основанные на PCRE, такие как `preg_replace()`. Это обеспечивает лучшую производительность и безопасность ваших приложений на PHP.

split()

Функция split() разбивает строку на элементы, границы которых определяются по заданному шаблону. Синтаксис функции split():

array split(string шаблон, string строка [, int порог]);

Необязательный параметр порог определяет максимальное количество элементов, на которые делится строка слева направо. Если шаблон содержит алфавитные символы, функция split() работает с учетом регистра символов. Следующий пример демонстрирует использование функции split() для разбиения канонического IP-адреса на триплеты:

$ip = "123.345.789.000"; // Канонический IP-адрес
$iparr = split("\.", $ip); // Поскольку точка является служебным символом, ее необходимо экранировать.
print "$iparr[0] <br>"; // Выводит "123"
print "$iparr[1] <br>"; // Выводит "345"
print "$iparr[2] <br>"; // Выводит "789"
print "$iparr[3] <br>"; // Выводит "000"

Примечание:
Функция `split()` устарела начиная с PHP 5.3.0 и не рекомендуется для использования. Вместо нее рекомендуется использовать функцию `explode()` или функции для работы с регулярными выражениями, такие как `preg_split()`. Используйте современные функции для обеспечения совместимости и безопасности вашего кода.

spliti()

Функция spliti() работает точно так же, как ее прототип split(), за одним исключением: она не учитывает регистра символов. Синтаксис функции spliti():

array spliti (string шаблон, string строка [, int порог]);

Разумеется, регистр символов важен лишь в том случае, если шаблон содержит алфавитные символы. Для других символов выполнение spliti() полностью аналогично split().

Примечание:
Функция `spliti()` устарела начиная с PHP 5.3.0 и не рекомендуется для использования. Вместо нее рекомендуется использовать функцию `preg_split()` с модификатором `i` для игнорирования регистра или функцию `explode()`. Используйте современные функции для обеспечения совместимости и безопасности вашего кода.

sql_regcase()

Вспомогательная функция sql_regcase() заключает каждый символ входной строки в квадратные скобки и добавляет к нему парный символ. Синтаксис функции sql_regcase():

string sql_regcase(string строка);

Если алфавитный символ существует в двух вариантах (верхний и нижний регистры), выражение в квадратных скобках будет содержать оба варианта; в противном случае исходный символ повторяется дважды. Функция sql_regcase() особенно удобна при использовании РНР с программными пакетами, поддерживающими регулярные выражения в одном регистре. Пример преобразования строки функцией sql_regcase():

$version = "php 4.0";
print sql_regcase($version);
// Выводится строка [Pp][Hh][Pp][ ][4].[0]

Примечание:
Функция `sql_regcase()` устарела начиная с PHP 5.3.0 и была удалена в PHP 7.0.0. Рекомендуется использовать другие методы и функции для достижения аналогичного функционала.

Синтаксис регулярных выражений в стиле Perl

Perl давно считается одним из лучших языков для обработки текстов. Синтаксис Perl позволяет осуществлять поиск и замену даже для самых сложных шаблонов. Разработчики PHP решили, что нет смысла изобретать что-то новое, когда уже существует проверенное временем решение. Таким образом, знаменитый синтаксис регулярных выражений Perl стал доступен для пользователей PHP. В результате появились функции для работы с регулярными выражениями в стиле Perl.

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

В этом разделе мы кратко рассмотрим диалект регулярных выражений Perl. В качестве примера рассмотрим следующий шаблон:

/food/

Обратите внимание, что слово "food" заключено между двумя косыми чертами. Как и в POSIX, вы можете создавать более сложные шаблоны с помощью квантификаторов:

/fo+/

Этот шаблон ищет последовательность "fo", за которой может следовать один или несколько символов "o". Так, этот шаблон будет соответствовать строкам "food", "fool" и "fooo". Рассмотрим другой пример с квантификатором:

/fo{2,4}/

Этот шаблон ищет символ "f", за которым следует от 2 до 4 символов "o". Таким образом, строки "fool", "foool" и "foooool" будут соответствовать этому шаблону.

В регулярных выражениях Perl доступны все квантификаторы, которые были упомянуты в разделе про регулярные выражения POSIX.

Метасимволы

Одной из интересных особенностей Perl является использование метасимволов при поиске. Метасимвол (следует отметить, что авторское толкование термина «метасимвол» противоречит не только всем традициям, но и официальной документации PHP — примеч. перев.) представляет собой алфавитный символ с префиксом \ — признаком особой интерпретации следующего символа. Например, метасимвол \d может использоваться при поиске денежных сумм:

/(\d+).000/

Комбинация \d обозначает любую цифру. Конечно, в процессе поиска часто возникает задача идентификации алфавитно-цифровых символов, поэтому в Perl для них был определен метасимвол \w:

/<([\w]+)>/

Этот шаблон совпадает с конструкциями, заключенными в угловые скобки, — например, тегами HTML. Кстати, метасимвол \W имеет прямо противоположный смысл и используется для идентификации символов, не являющихся алфавитно-цифровыми.

Еще один полезный метасимвол, \b, совпадает с границами слов:

/sa\b/

Поскольку метасимвол границы слова расположен справа от текста, этот шаблон совпадет в строках salsa и lisa, но не в строке sand. Противоположный метасимвол, \B, совпадает с чем угодно, кроме границы слова:

/sa\B/

Шаблон совпадает в таких строках, как sand и Sally, но не совпадает в строке salsa.

Модификаторы

Модификаторы заметно упрощают работу с регулярными выражениями. Впрочем, модификаторов много, и в таблице 8.1 приведены лишь наиболее интересные из них. Модификаторы перечисляются сразу же после регулярного выражения — например, /string/i.

Таблица 8.1. Примеры модификаторов

МодификаторОписание
mФрагмент текста интерпретируется как состоящий из нескольких «логических строк». По умолчанию специальные символы ^ и $ совпадают только в начале и в конце всего фрагмента. При включении «многострочного режима» при помощи модификатора m, ^ и $ будут совпадать в начале и в конце каждой логической строки внутри фрагмента.
sПо смыслу противоположен модификатору m — весь фрагмент интерпретируется как одна строка. Символ «.» совпадает со всеми символами, включая символы новой строки.
iПоиск выполняется без учета регистра символов.

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

Эти функции подробно описаны в следующих разделах.

preg_match()

Функция preg_match() ищет в заданной строке совпадение для шаблона. Если совпадение найдено, возвращается TRUE, в противном случае возвращается FALSE. Синтаксис функции preg_match():

int preg_match (string шаблон, string строка [, array совпадения])

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

$line = "Vi is the greatest word processor ever created!";
// Выполнить поиск слова "Vi" без учета регистра символов:
if (preg_match("/\bVi\b/i", $line, $match)) :
    print "Match found!";
endif;
// Команда if в этом примере возвращает TRUE

preg_match_all()

Функция preg_match_all() находит все совпадения шаблона в заданной строке.

Синтаксис функции preg_match_all():

int preg_match_all(string шаблон, string строка, array совпадения [, int порядок]);

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

Следующий пример показывает, как при помощи функции preg_match_all() найти весь текст, заключенный между тегами HTML <b>...</b>:

$user_info = "Name: <b>Rasmus Lerdorf</b> <br> Title: <b>PHP Guru</b>";
preg_match_all("/<b>(.*?)<\/b>/U", $user_info, $pat_array);
print $pat_array[0][0]." <br> ".$pat_array[0][1]."\n";

Результат:

Rasmus Lerdorf
PHP Guru

preg_replace()

Функция preg_replace() работает точно так же, как и ereg_replace(), за одним исключением — регулярные выражения могут использоваться в обоих параметрах, шаблон и замена. Синтаксис функции preg_replace():

mixed preg_replace(mixed шаблон, mixed замена, mixed строка [, int порог]);

Необязательный параметр порог определяет максимальное количество замен в строке. Интересный факт: параметры шаблон и замена могут представлять собой массивы. Функция preg_replace() перебирает элементы обоих массивов и выполняет замену по мере их нахождения.

preg_split()

Функция preg_split() аналогична split() за одним исключением — параметр шаблон может содержать регулярное выражение. Синтаксис функции preg_split():

array preg_split(string шаблон, string строка [, int порог [, int флаги]]);

Необязательный параметр порог определяет максимальное количество элементов, на которые делится строка. В следующем примере функция preg_split() используется для выборки информации из переменной.

$user_info = "+wj+++Gilmore+++++wjgi]more@hotmail.com+++++++Columbus+++OH";
$fields = preg_split("/\+{1,}/", $user_info);
$x = 0;
while ($x < sizeof($fields)):
    print $fields[$x]. "<br>";
    $x++;
endwhile;

Результат:

WJ
Gilmore
wjgilmore@hotmail.com
Columbus
OH

preg_grep()

Функция preg_grep() перебирает все элементы заданного массива и возвращает все элементы, в которых совпадает заданное регулярное выражение. Синтаксис функции preg_grep():

array preg_grep(string шаблон, array массив);

Пример использования функции preg_grep() для поиска в массиве слов, начинающихся на "р":

$foods = array("pasta", "steak", "fish", "potatoes");
// Поиск элементов, начинающихся с символа "р"
// за которым следует один или несколько символов
$p_foods = preg_grep("/^p(\w+)/", $foods);
$x = 0;
while ($x < sizeof($p_foods)):
    print $p_foods[$x]. "<br>";
    $x++;
endwhile;

Результат:

pasta
potatoes

Другие строковые функции

Кроме функций для работы с регулярными выражениями, описанными в первой части этой главы, в PHP существует более 70 функций для выполнения практически всех мыслимых операций со строками. Подробное перечисление и описание всех функций выходит за рамки этой книги и приведет к обычному повторению информации, приведенной в документации PHP. По этой причине я решил оформить оставшуюся часть главы в виде списка FAQ из вопросов, часто встречающихся в электронных конференциях по PHP и на тематических сайтах. На мой взгляд, такой подход позволяет более эффективно представить общие принципы обширной библиотеки строковых функций PHP.

Дополнение и сжатие строк

При форматировании текста иногда требуется изменить длину строки путем добавления или удаления символов. В PHP для этой задачи предусмотрено несколько функций.

chop()

Функция chop() удаляет из строки завершающие пропуски и символы новой строки. Синтаксис функции chop():

string chop(string строка);

В приведенном ниже примере функция chop() удаляет лишние символы новой строки:

$header = "Table of Contents\n\n";
$header = chop($header);
// $header теперь содержит "Table of Contents"

str_pad()

Функция str_pad() выравнивает строку до определенной длины заданными символами и возвращает отформатированную строку. Синтаксис функции str_pad():

string str_pad(string строка, int длина_дополнения [, string дополнение [, int тип_дополнения]]);

Если необязательный параметр дополнение не указан, строка дополняется пробелами. В противном случае строка дополняется заданными символами. По умолчанию строка дополняется справа; тем не менее, вы можете передать в параметре тип_дополнения константу STR_PAD_RIGHT, STR_PAD_LEFT или STR_PAD_BOTH, что приведет к дополнению строки в заданном направлении.

$food = "salad";
print str_pad($food, 5); // Выводит строку "salad"

$header = "Table of Contents";
print str_pad($header, 23, "=+=+=", STR_PAD_BOTH);
// Вывод: =+=+=Table of Contents=+=+=

trim()

Функция trim() удаляет все пропуски с обоих краев строки и возвращает полученную строку. Синтаксис функции trim():

string trim(string строка);

К числу удаляемых пропусков относятся и специальные символы \n, \r, \t, \v и \0.

ltrim()

Функция ltrim() удаляет все пропуски и специальные символы с левого края строки и возвращает полученную строку. Синтаксис функции ltrim():

string ltrim(string строка);

Функция удаляет те же специальные символы, что и функция trim().

Определение длины строки

Длину строки в символах можно определить при помощи функции strlen(). Синтаксис функции strlen():

int strlen(string строка);

Следующий пример демонстрирует определение длины строки функцией strlen():

$string = "hello";
$length = strlen($string);
// $length = 5

Сравнение двух строк

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

Все эти функции подробно описаны в следующих разделах.

strcmp()

Функция strcmp() сравнивает две строки с учетом регистра символов. Синтаксис функции strcmp():

int strcmp(string строка1, string строка2);

После завершения сравнения strcmp() возвращает одно из трех возможных значений:

$string1 = "butter";
$string2 = "butter";

if (strcmp($string1, $string2) == 0) {
    print "Strings are equivalent!";
}

// Команда if возвращает TRUE

strcasecmp()

Функция strcasecmp() работает точно так же, как strcmp(), за одним исключением — регистр символов при сравнении не учитывается. Синтаксис функции strcasecmp():

int strcasecmp(string строка1, string строка2);
$string1 = "butter";
$string2 = "Butter";

if (strcasecmp($string1, $string2) == 0) {
    print "Strings are equivalent!";
}
// Команда if возвращает TRUE

strspn()

Функция strspn() возвращает длину первого сегмента строки1, содержащего символы, присутствующие в строке2. Синтаксис функции strspn():

int strspn(string строка1, string строка2);

Следующий фрагмент показывает, как функция strspn() используется для проверки пароля:

$password = "12345";
if (strspn($password, "1234567890") != strlen($password)):
    print "Password cannot consist solely of numbers!";
endif;

strcspn()

Функция strcspn() возвращает длину первого сегмента строки1, содержащего символы, отсутствующие в строке2. Синтаксис функции strcspn():

int strcspn(string строка1, string строка2);

В следующем фрагменте функция strcspn() используется для проверки пароля:

$password = "12345";
if (strcspn($password, "1234567890") == 0):
    print "Password cannot consist solely of numbers!";
endif;

Обработка строковых данных без применения регулярных выражений

При обработке больших объемов информации функции регулярных выражений сильно замедляют выполнение программы. Эти функции следует применять лишь при обработке относительно сложных строк, в которых регулярные выражения действительно необходимы. Если же анализ текста выполняется по относительно простым правилам, можно воспользоваться стандартными функциями РНР, которые заметно ускоряют обработку. Все эти функции описаны ниже.

strtok()

Функция strtok() разбивает строку на лексемы по разделителям, заданным вторым параметром. Синтаксис функции strtok():

string strtok(string строка, string разделители)

У функции strtok() есть одна особенность: чтобы полностью разделить строку, функцию необходимо последовательно вызвать несколько раз. При очередном вызове функция выделяет из строки следующую лексему. Параметр строка задается всего один раз — функция отслеживает текущую позицию в строке до тех пор, пока строка не будет полностью разобрана на лексемы или не будет задан новый параметр строка. Пример разбиения строки по нескольким разделителям:

$info = "WJ Gilmore:wjgilmore@hotmail.com | Columbus, Ohio";
// Ограничители - двоеточие (:), вертикальная черта (|) и запятая (,)
$tokens = ":|,";
$tokenized = strtok($info, $tokens);

// Вывести элементы разбивки
while ($tokenized):
    echo "Element = $tokenized<br>";
    // Обратите внимание: при последующих вызовах strtok первый аргумент не передается
    $tokenized = strtok($tokens);
endwhile;

Результат:

Element = WJ Gilmore
Element = wjgilmore@hotmail.com
Element = Columbus
Element = Ohio

parse_str()

Функция parse_str() выделяет в строке пары «переменная-значение» и присваивает значения переменных в текущей области видимости. Синтаксис функции parse_str():

void parse_str(string строка);

Функция parse_str() особенно удобна при обработке URL, содержащих данные форм HTML или другую расширенную информацию. В следующем примере анализируется информация, переданная через URL. Строка представляет собой стандартный способ передачи данных между страницами либо откомпилированных в гиперссылке, либо введенных в форму HTML:

$url = "fname=wj&lname=gilmore&zip=43210";
parse_str($url);
// После выполнения parse_str() доступны следующие переменные:
// $fname = "wj";
// $lname = "gilmore";
// $zip = "43210";

Поскольку эта функция создавалась для работы с URL, она игнорирует символ амперсанд (&).

Работа с формами HTML в РНР описана в главе 10.

explode()

Функция explode() делит строку на элементы и возвращает эти элементы в виде массива. Синтаксис функции explode():

array explode(string разделитель, string строка [, int порог]);

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

$info = "wilson | baseball | indians";
$user = explode("|", $info);
// $user[0] = "wilson";
// $user[1] = "baseball";
// $user[2] = "Indians";

Функция explode() практически идентична функции регулярных выражений POSIX split(), описанной выше. Главное различие заключается в том, что передача регулярных выражений в параметрах допускается только при вызове split().

implode()

Если функция explode() разделяет строку на элементы массива, то её двойник — функция implode() — объединяет массив в строку. Синтаксис функции implode():

string implode(string разделитель, array фрагменты);

Формирование строки из массива продемонстрировано в следующем примере:

$ohio_cities = array("Columbus", "Youngstown", "Cleveland", "Cincinnati");
$city_string = implode(" | ", $ohio_cities);
// $city_string = "Columbus | Youngstown | Cleveland | Cincinnati";

У функции implode() имеется псевдоним — функция join().

strpos()

Функция strpos() находит в строке первый экземпляр заданной подстроки. Синтаксис функции strpos():

int strpos(string строка, string подстрока [, int смещение]);

Необязательный параметр смещение задает позицию, с которой должен начинаться поиск. Если подстрока не найдена, strpos() возвращает FALSE (0).

$log = "206.169.23.11:/www/:2000-08-10
206.169.23.11:/www/logs/:2000-02-04
206.169.23.11:/www/img/:1999-01-31";

// В какой позиции в журнале впервые встречается 1999 год?
$pos = strpos($log, "1999");

// $pos = 95, поскольку первый экземпляр "1999"
// находится в позиции 95 строки, содержащейся в переменной $log

strrpos()

Функция strrpos() находит в строке последний экземпляр заданного символа. Синтаксис функции strrpos():

int strrpos(string строка, char символ);

По возможностям эта функция уступает своему двойнику — функции strpos(), поскольку она позволяет искать только отдельный символ, а не всю строку. Если во втором параметре strrpos() передается строка, при поиске будет использован только ее первый символ.

str_replace()

Функция str_replace() ищет в строке все вхождения заданной подстроки и заменяет их новой подстрокой. Синтаксис функции str_replace():

string str_replace(string подстрока, string замена, string строка);

Функция substr_replace(), описанная ниже в этом разделе, позволяет провести замену лишь в определенной части строки. Ниже показано, как функция str_replace() используется для проведения глобальной замены в строке.

Если подстрока ни разу не встречается в строке, исходная строка не изменяется:

$favorite_food = "My favorite foods are ice cream and chicken wings";
$favorite_food = str_replace("chicken wings", "pizza", $favorite_food);
// $favorite_food = "My favorite foods are ice cream and pizza"

strstr()

Функция strstr() возвращает часть строки, начинающуюся с первого вхождения заданной подстроки. Синтаксис функции strstr():

string strstr(string строка, string подстрока);

В следующем примере функция strstr() используется для выделения имени домена из URL:

$url = "http://www.apress.com";
$domain = strstr($url, ".");
// $domain = ".apress.com"

substr()

Функция substr() возвращает часть строки, начинающуюся с заданной начальной позиции и имеющую заданную длину. Синтаксис функции substr():

string substr(string строка, int начало [, int длина]);

Если необязательный параметр длина не указан, считается, что подстрока начинается с заданной начальной позиции и продолжается до конца строки. При использовании этой функции необходимо учитывать четыре обстоятельства:

Помните о том, что параметр начало определяет смещение от первого символа строки; таким образом, возвращаемая строка в действительности начинается с символа с номером (начало + 1).

Следующий пример демонстрирует выделение части строки функцией substr():

$car = "1944 Ford";
$model = substr($car, 5);
// $model = "Ford"

Пример с положительным параметром длина:

$car = "1944 Ford";
$model = substr($car, 0, 4);
// $model = "1944"

Пример с отрицательным параметром длина:

$car = "1944 Ford";
$model = substr($car, 2, -5);
// $model = "44"

substr_count()

Функция substr_count() возвращает количество вхождений подстроки в заданную строку. Синтаксис функции substr_count():

int substr_count(string строка, string подстрока);

В следующем примере функция substr_count() подсчитывает количество вхождений подстроки "ain":

$tongue_twist = "The rain falls mainly on the plains of Spain";
$count = substr_count($tongue_twist, "ain");
// $count = 4

substr_replace()

Функция substr_replace() заменяет часть строки, которая начинается с заданной позиции. Если задан необязательный параметр длина, заменяется фрагмент заданной длины; в противном случае производится замена по всей длине заменяющей строки. Синтаксис функции substr_replace():

string substr_replace(string строка, string замена, int начало [, int длина]);

Параметры начало и длина задаются по определенным правилам:

Простая замена текста функцией substr_replace() продемонстрирована в следующем примере:

$favs = " 's favorite links";
$name = "Alessia";
// Параметры "0, 0" означают, что заменяемый фрагмент начинается
// и завершается в первой позиции строки.
$favs = substr_replace($favs, $name, 0, 0);
print $favs;

Результат:

Alessia's favorite links

Преобразование строк и файлов к формату HTML и наоборот

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

Преобразование текста в HTML

Быстрое преобразование простого текста к формату web-браузера — весьма распространенная задача. В ее решении вам помогут функции, описанные в этом разделе.

nl2br()

Функция nl2br() заменяет все символы новой строки (\n) эквивалентными конструкциями HTML <br>.

string nl2br(string строка);

Символы новой строки могут быть как видимыми (то есть явно включенными в строку), так и невидимыми (например, введенными в редакторе). В следующем примере текстовая строка преобразуется в формат HTML посредством замены символов \n разрывами строк:

// Текстовая строка, отображаемая в редакторе.
$text_recipe = "Party Sauce recipe:
1 can stewed tomatoes
3 tablespoons fresh lemon juice
Stir together, server cold.";

// Преобразовать символы новой строки в <br>
$html_recipe = nl2br($text_recipe);

При последующем выводе $html_recipe браузеру будет передан следующий текст в формате HTML:

Party Sauce recipe:<br>1 can stewed tomatoes<br>3 tablespoons fresh lemon juice<br>Stir together, server cold.<br>

htmlentities()

Функция htmlentities() преобразует символы в эквивалентные конструкции HTML. Синтаксис функции htmlentities:

string htmlentities(string строка);

В следующем примере производится необходимая замена символов строки для вывода в браузере:

$user_input = "The cookbook, entitled Cafe Francaise' costs < $42.25.";
$converted_input = htmlentities($user_input);
// $converted_input = "The cookbook, entitled 'Caf&egrave; Frac&ccediliaise' costs &lt; $42.25.";

Функция htmlentities() в настоящее время работает только для символов кодировки ISO-8559-1 (ISO-Latin-1). Кроме того, она не преобразует пробелы в &nbsp;, как следовало бы ожидать.

htmlspecialchars()

Функция htmlspecialchars() заменяет некоторые символы, имеющие особый смысл в контексте HTML, эквивалентными конструкциями HTML. Синтаксис функции htmlspecialchars():

string htmlspecialchars(string строка);

Функция htmlspecialchars() в настоящее время преобразует следующие символы:

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

$user_input = "I just can't get «enough» of PHP & those fabulous cooking recipes!";
$conv_input = htmlspecialchars($user_input);
// $conv_input = "I just can't &lt;&lt;enough&gt;&gt; of PHP &amp; those fabulous cooking recipes!";

Если функция htmlspecialchars() используется в сочетании с nl2br(), то последнюю следует вызывать после htmlspecialchars(). В противном случае конструкции <br>, сгенерированные при вызове nl2br(), преобразуются в видимые символы.

get_html_translation_table()

Функция get_html_translation_table() обеспечивает удобные средства преобразования текста в эквиваленты HTML. Синтаксис функции get_html_translation_table():

string get_html_translation_table(int таблица);

Функция get_html_translation_table() возвращает одну из двух таблиц преобразования (определяется параметром таблица), используемых в работе стандартных функций htmlspecialchars() и htmlentities(). Возвращаемое значение может использоваться в сочетании с другой стандартной функцией, strtr(), для преобразования текста в код HTML.

Параметр таблица принимает одно из двух значений:

В следующем примере функция get_html_translation_table() используется при преобразовании текста в код HTML:

$string = "La pasta e il piatto piu amato in Italia";
$translate = get_html_translation_table(HTML_ENTITIES);
print strtr($string, $translate);
// Специальные символы преобразуются в конструкции HTML и правильно отображаются в браузере.

Кстати, функция array_flip() позволяет провести преобразование текста в HTML в обратном направлении и восстановить исходный текст. Предположим, что вместо вывода результата strtr() в предыдущем примере мы присвоили его переменной $translated_string.

$translate = array_flip($translate);
$translated_string = "La pasta &eacute; il piatto pi&uacute; amato in Italia";
$original_string = strtr($translated_string, $translate);
// $original_string = "La pasta e il piatto piu amato in Italia";

strtr()

Функция strtr() транслирует строку, то есть заменяет в ней все символы, входящие в строку источник, соответствующими символами строки приемник. Синтаксис функции strtr():

string strtr(string строка, string источник, string приемник);

Если строки источник и приемник имеют разную длину, длинная строка усекается до размеров короткой строки.

Существует альтернативный синтаксис вызова strtr() с двумя параметрами; в этом случае второй параметр содержит ассоциативный массив, ключи которого соответствуют заменяемым подстрокам, а значения — заменяющим подстрокам. В следующем примере теги HTML заменяются XML-подобными конструкциями:

$source = array("<title>" => "<h1>", "</title>" => "</h1>");
$string = "<h1>Today In PHP-Powered News</h1>";
print strtr($string, $source);
// Выводится строка "<title>Today in PHP-Powered News</title>"

Преобразование HTML в простой текст

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

strip_tags()

Функция strip_tags() удаляет из строки все теги HTML и РНР, оставляя в ней только текст. Синтаксис функции strip_tags():

string strip_tags(string строка [, string разрешенные_теги]);

Необязательный параметр разрешенные_теги позволяет указать теги, которые должны пропускаться в процессе удаления.

Ниже приведен пример удаления из строки всех тегов HTML функцией strip_tags():

$user_input = "I just <b>love</b> РНР and <i>gourment</i> recipes!";
$stripped_input = strip_tags($user_input);
// $stripped_input = "I just love PHP and gourmet recipes!";

В следующем примере удаляются не все, а лишь некоторые теги:

$input = "I <b>love</b> to <a href = \"http://www.eating.com\">eat!</a>!";
$strip_input = strip_tags($user_input, "<a>");
// $strip_input = "I love to <a href = \"http://www.eating.com\">eat!</a>!";

Удаление тегов из текста также производится функцией fgetss(), описанной в главе 7.

get_meta_tags()

Хотя функция get_meta_tags() и не имеет прямого отношения к преобразованию текста, это весьма полезная функция, о которой следует упомянуть. Синтаксис функции get_meta_tags():

array get_meta_tags(string имя_файла/URL [, int включение_пути]);

Функция get_meta_tags() предназначена для поиска в файле HTML тегов META.

Теги META содержат информацию о странице, используемую главным образом поисковыми системами. Эти теги находятся внутри пары тегов <head>...</head>. Применение тегов META продемонстрировано в следующем фрагменте:

<html>
<head>
<title>PHP Recipes</title>
<meta name="keywords" content="gourmet, PHP, food, code, recipes, chef, programming, web">
<meta name="description" content="PHP Recipes provides savvy readers with the latest in PHP programming and gourmet cuisine!">
<meta name="author" content="WJ Gilmore">
</head>

Функция get_meta_tags() ищет в заголовке документа теги, начинающиеся словом META, и сохраняет имена тегов и их содержимое в ассоциативном массиве. Пример применения этой функции:

$meta_tags = get_meta_tags("example.php");
// Переменная $meta_tags содержит массив со следующей информацией:
// $meta_tags["keywords"] = "gourmet, PHP, food, code, recipes, chef, programming, Web";
// $meta_tags["description"] = "PHP Recipes provides savvy readers with the latest in PHP programming and gourmet cuisine";
// $meta_tags["author"] = "WJ Gilmore";

Интересная деталь: данные тегов META можно извлекать не только из файлов, находящихся на сервере, но и из других URL.

Теги META и их использование прекрасно описаны в статье Джо Берна (Joe Burn) «So, You Want a Meta Command, Huh?» на сайте HTML Goodies.

Преобразование строки к верхнему и нижнему регистру

В PHP существуют функции для изменения регистра строки:

Рассмотрим их подробнее.

strtolower()

Функция strtolower() преобразует все алфавитные символы строки к нижнему регистру. Неалфавитные символы функцией не изменяются. Синтаксис функции:

string strtolower(string строка);

Пример преобразования строки к нижнему регистру:

$sentence = "COOKING and PROGRAMMING PHP are my TWO favorite pastimes!";
$sentence = strtolower($sentence);
// После вызова функции $sentence содержит строку "cooking and programming php are my two favorite pastimes!"

strtoupper()

Строки можно преобразовывать не только к нижнему, но и к верхнему регистру. Преобразование выполняется функцией strtoupper().

string strtoupper(string строка);

Неалфавитные символы функцией не изменяются. Пример преобразования строки к верхнему регистру:

$sentence = "cooking and programming PHP are my two favorite pastimes!";
$sentence = strtoupper($sentence);
// Результат: "COOKING AND PROGRAMMING PHP ARE MY TWO FAVORITE PASTIMES!"

ucfirst()

Функция ucfirst() преобразует к верхнему регистру первый символ строки, если он является алфавитным символом.

string ucfirst(string строка);

Пример преобразования первого символа строки:

$sentence = "cooking and programming PHP are my two favorite pastimes!";
$sentence = ucfirst($sentence);
// Результат: "Cooking and programming PHP are my two favorite pastimes!"

ucwords()

Функция ucwords() преобразует к верхнему регистру первую букву каждого слова в строке.

string ucwords(string строка);

«Слово» определяется как последовательность символов, отделенная от других элементов строки пробелами. Пример преобразования первых символов слов:

$sentence = "cooking and programming PHP are my two favorite pastimes!";
$sentence = ucwords($sentence);
// Результат: "Cooking And Programming PHP Are My Two Favorite Pastimes!"

Проект: идентификация браузера

Каждый программист, пытающийся создать удобный web-сайт, должен учитывать различия в форматировании страниц при просмотре сайта в разных браузерах и операционных системах. Хотя консорциум W3 продолжает публиковать стандарты, которых должны придерживаться программисты при создании web-приложений, разработчики браузеров любят дополнять эти стандарты своими маленькими «усовершенствованиями», что в конечном счете вызывает хаос и путаницу. Разработчики часто решают эту проблему, создавая разные страницы для каждого типа браузера и операционной системы — при этом объем работы значительно увеличивается, но зато итоговый сайт идеально подходит для любого пользователя. Результат — хорошая репутация сайта и уверенность в том, что пользователь посетит его снова.

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

Приведенный ниже проект (sniffer.php) показывает, как использовать функции РНР для работы с регулярными выражениями с целью получения информации по запросам. Программа определяет тип и версию браузера и операционной системы, после чего выводит полученную информацию в окне браузера. Но прежде чем переходить к непосредственному анализу программы, я хочу представить один из главных ее компонентов — стандартную переменную РНР $HTTP_USER_AGENT. В этой переменной в строковом формате хранятся различные сведения о браузере и операционной системе пользователя — именно то, что нас интересует. Эту информацию можно легко вывести на экран всего одной командой:

echo $HTTP_USER_AGENT;

При работе в Internet Explorer 5.0 на компьютере с Windows 98 результат будет выглядеть так:

Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)

Для Netscape Navigator 4.75 выводятся следующие данные:

Mozilla/4.75 (Win98; U)

Sniffer.php извлекает необходимые данные из $HTTP_USER_AGENT при помощи функций обработки строк и регулярных выражений. Алгоритм программы на псевдокоде:

preg_split()

<?php
/*
Файл : sniffer.php
Назначение: Идентификация типа/версии браузера и платформы
Автор: В. Дж. Гилмор
Дата : 24 августа 2000 г.
*/

// Функция: browser_info
// Назначение: Возвращает тип и версию браузера
function browser_info ($agent) {
    // Определить тип браузера
    // Искать сигнатуру Internet Explorer
    if (ereg('MSIE ([0-9].[0-9]{1,2})', $agent, $version)) {
        $browse_type = "IE";
        $browse_version = $version[1];
    }
    // Искать сигнатуру Opera
    elseif (ereg('Opera ([0-9].[0-9]{1,2})', $agent, $version)) {
        $browse_type = "Opera";
        $browse_version = $version[1];
    }
    // Искать сигнатуру Netscape. Проверка браузера Netscape
    // *должна* выполняться после проверки Internet Explorer и Opera,
    // поскольку все эти браузеры любят сообщать имя
    // Mozilla вместе с настоящим именем.
    elseif (ereg('Mozilla/([0-9].[0-9]{1,2})', $agent, $version)) {
        $browse_type = "Netscape";
        $browse_version = $version[1];
    }
    // Если это не Internet Explorer, Opera или Netscape.
    // значит, мы обнаружили неизвестный браузер,
    else {
        $browse_type = "Unknown";
        $browse_version = "Unknown";
    }
    // Вернуть тип и версию браузера в виде массива
    return array ($browse_type, $browse_version);
} // Конец функции browser_info

// Функция: opsys_info
// Назначение: Возвращает информацию об операционной системе пользователя
function opsys_info($agent) {
    // Идентифицировать операционную систему
    // Искать сигнатуру Windows
    if (strstr($agent, 'win')) {
        $opsys = "windows";
    }
    // Искать сигнатуру Linux
    elseif (strstr($agent, 'Linux')) {
        $opsys = "Linux";
    }
    // Искать сигнатуру UNIX
    elseif (strstr($agent, 'Unix')) {
        $opsys = "Unix";
    }
    // Искать сигнатуру Macintosh
    elseif (strstr($agent, 'Mac')) {
        $opsys = "Macintosh";
    }
    // Неизвестная платформа
    else {
        $opsys = "Unknown";
    }
    // Вернуть информацию об операционной системе
    return $opsys;
} // Конец функции opsys_info

// Сохранить возвращаемый массив в списке
list ($browse_type, $browse_version) = browser_info($HTTP_USER_AGENT);
$operating_sys = opsys_info($HTTP_USER_AGENT);

print "Browser Type: $browse_type <br>";
print "Browser Version: $browse_version <br>";
print "Operating System: $operating_sys <br>";
?>

Вот и все! Например, если пользователь работает в браузере Netscape 4.75 на компьютере с системой Windows, будет выведен следующий результат:

Browser Type: Netscape

Browser Version: 4.75

Operating System: Windows

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

Итоги

В этой главе был изложен довольно обширный материал. Какой прок от языка программирования, если в нем нельзя работать с текстом? Мы рассмотрели следующие темы:

Следующая глава открывает вторую часть книги — кстати, мою любимую. В ней мы начнем знакомиться со средствами РНР, ориентированными на Web, рассмотрим процесс динамического создания содержимого, включение файлов и построение общих шаблонов. В дальнейших главах части 2 рассматриваются работа с формами HTML, базы данных, отслеживание данных сеанса и нетривиальные средства работы с шаблонами. Держитесь — начинается самое интересное!