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

Котеров Д. В., Костарев А. Ф. - PHP 5. 2-е издание (В подлиннике) - 2008

.pdf
Скачиваний:
6114
Добавлен:
29.02.2016
Размер:
11.36 Mб
Скачать

264

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

текст? В третьей и последующих версиях интерпретатора разработчики отказались от этого механизма и объявили, что + следует применять только для сложения чисел, и никак иначе. Что же касается конкатенации строк, то для нее ввели специальный оператор "." (точка).

Оператор "." всегда воспринимает свои операнды как строки и возвращает строку. Если один из операндов не может быть переведен в строковое представление, т. е. если это массив или объект, то он воспринимается как строки array и object соответственно. Вообще говоря, это правило применимо и не только при сцеплении строк, но и при передаче такого операнда в какую-нибудь стандартную функцию, которой требуется строка. Например, следующие команды выведут слово Array:

$a = array(10, 20, 30);

echo $a // Внимание! Неожиданный результат!

Есть и другой, более специализированный, способ конкатенации строк. Он обычно используется, когда значения строковых или числовых переменных перемежаются с обычными словами. Если, к примеру, мы в программе работаем с датой и временем, представленными совокупностью переменных ($day, $month, $year, $hour, $min, $sec), то вывести строку вида "Все началось 19 февраля 1998 года, в 13:24:18" можно так:

echo "Все началось $day $month $year года, в $hour:$min:$sec";

При этом в строку, вырабатываемую инструкцией echo, автоматически в нужных местах вставятся значения наших переменных. Это позволяет констатировать тот факт, что в PHP все переменные начинаются с $.

string str_repeat(string $st, string $number)

Функция "повторяет" строку $st $number раз и возвращает объединенный результат. Вот пример:

echo str_repeat("test!",3); // выводит test!test!test!

О сравнении строк

Теперь мы хотели бы рассмотреть одно тонкое место в интерпретаторе PHP, касающееся работы со строками. (Собственно, мы уже затрагивали эту тему, когда говорили об операторах сравнения.) Если мы используем операторы сравнения == и != (или любые другие, которые могут потребовать перевода строки в число) с операндами-строками, то результат, вопреки ожиданиям, не всегда оказывается верным. Чаще всего это проявляется как раз в инструкции if. Примеры приведены в листинге 15.1.

Листинг 15.1. Файл compare.php

<?php ## Особенности операторов сравнения применительно к строкам. $one = 1; // Число один.

$zero = 0; // Присваиваем число нуль.

if ($one == "") echo 1; // Очевидно, не равно — не выводит 1.

Глава 15. Строковые функции

265

if ($zero == "") echo 2; //* Внимание! Вопреки ожиданиям печатает 2! if ("" == $zero) echo 3; //* И это тоже не поможет — печатает!..

if ("$zero" == "") echo 4; // Так правильно.

if (strval($zero) == "") echo 5; // Так тоже правильно — не выводит 5. if ($zero === "") echo 6; // Лучший способ, но не действует в PHP 3. ?>

Данная программа напечатает строку "23", а значит, срабатывают только второй и третий операторы echo (помечены звездочками). А именно, PHP считает, что 0=="", а также ""==0.

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

Получается, что в операциях сравнения пустая строка "" прежде всего трактуется как 0 (ноль) и уж затем — как "пусто"? Это звучит довольно парадоксально, но это действительно так. Операнды сравниваются как строки только в том случае, если они оба — строки. Если же хотя бы один из операндов — не строка, но может трактоваться как false, PHP использует логический контекст при сравнении. Пустая строка воспринимается как false, а false == 0, поэтому мы и получаем приведенный выше результат.

Итак, если вы хотите сравнить две переменные-строки, нужно быть абсолютно уверенными, что их типы именно строковые, а не числовые.

Впрочем, это не распространяется на оператор PHP === (тройное равенство, или оператор эквивалентности). Его использование заставляет интерпретатор всегда сравнивать величины и по значению, и по их типу. Итак, с точки зрения PHP 0=="", но 0!=="". Если вы не собираетесь программировать на PHP версии, ниже четвертой, рекомендуем всегда использовать === вместо strval(), как это было сделано в листинге 15.1.

Особенности strpos()

Существует одна стандартная ошибка, которую делают многие. Вот в чем она состоит. Есть такая функция — strpos($str,$what), которая возвращает позицию подстроки $what в строке $str или false, если подстрока не найдена. Пусть нам нужно проверить, встречается ли в некоторой строке $str подстрока <? (и напечатать "это PHP-программа", если встречается). Как мы знаем, вариант

if (strpos($str,"<?") != false)

echo "это PHP-программа";

не годится, если <? находится в самом начале строки (в этом случае не будет выдано наше сообщение, хотя подстрока в действительности найдена, и функция возвратила 0, а не false).

Если вы еще собираетесь работать с PHP версии 3, указанную проблему можно решить так:

266

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

if (strval(strpos($str,"<?")) != "")

echo "это PHP-программа";

Конечно, выглядит это немного "накручено", зато действительно работает. Приятно отметить, что в PHP версий 4 и 5 проблема решается гораздо более изящным образом:

if (strpos($str,"<?") !== false)

echo "это PHP-программа";

Рекомендуем всегда применять последний способ.

Обратите внимание, что мы используем оператор !== именно с константой false, а не с пустой строкой "". Вспомните, что для этого оператора false!=="", в то время как, разумеется, false=="".

Работа с одиночными символами

string chr(int $code)

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

Листинг 15.2. Файл chartable.php

<?php ## Печать всей таблицы ASCII-символов.

//Сначала создаем массив того, что мы собираемся выводить,

//не заботясь о форматировании (дизайне) информации

for ($i=0,$x=0; $x<16; $x++) { for ($y=0; $y<16; $y++) {

$chars[$x][$y] = array($i, chr($i)); $i++;

}

}

//Теперь выводим накопленную информацию, используя идеологию

//вставки участков кода в HTML-документ

?>

<table border=1 cellpadding=1 cellspacing=0> <?foreach ($chars as $row) {?>

<tr>

<?foreach ($row as $cell) { ?> <td>

<?=$cell[0]?>: <b><tt><?=$cell[1]?></tt></b>

</td>

Глава 15. Строковые функции

267

<?}?>

</tr>

<?}?>

</table>

Если код символа, который нужно вставить, точно известен, то можно обойтись и без функции chr(). Например, символ с кодом 1 можно вставить в строку так:

$str = "Это строка \x01 со специальным символом.";

Как видите, используются двойные кавычки (не апострофы!) и синтаксис \xNN, где NN — шестнадцатеричный код символа.

int ord(char $ch)

Эта функция, наоборот, возвращает код символа в $ch. Например, ord(chr($n)) всегда равно $n — конечно, если $n заключено между нулем и числом 255 (включительно).

Отрезание пробелов

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

Иногда трудно даже представить, какими могут быть странными пользователи, если дать им в руки клавиатуру и попросить напечатать на ней какое-нибудь слово. Так как клавиша <Пробел> — самая большая, то пользователи имеют обыкновение нажимать ее в самые невероятные моменты. Этому способствует также и тот факт, что символ с кодом 32, обозначающий пробел, как вы знаете, на экране не виден. Если программа не способна обработать описанную ситуацию, то она, в лучшем случае, после тягостного молчания отобразит в браузере что-нибудь типа "неверные входные данные", а в худшем — сделает при этом что-нибудь необратимое.

Между тем обезопасить себя от паразитных пробелов чрезвычайно просто, и разработчики PHP предоставляют нам для этого ряд специализированных функций. Не волнуйтесь о том, что их применение замедляет программу. Эти функции работают с молниеносной скоростью, а главное, одинаково быстро, независимо от объема переданных им строк. Конечно, мы не призываем к пароноидальному применению функций "отрезания" на каждой строчке программы, но в то же время, если есть хоть 1%-ное предположение, что строка может содержать лишние пробелы, следует без колебаний от них избавляться. В конце концов, отсекать пробелы один раз или тысячу — все равно, а вот не отрезать совсем и отрезать однажды — большая разница. Кстати, если отделять нечего, описанные ниже функции мгновенно заканчивают свою работу, так что их вызов обходится совсем дешево.

268

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

string trim(string $st [,string $charlist])

Возвращает копию $st, только с удаленными ведущими и концевыми пробельными символами. Под пробельными символами здесь и далее мы подразумеваем:

пробел " ";

символ перевода строки \n;

символ возврата каретки \r;

символ табуляции \t.

Установить альтернативный набор пробельных символов можно при помощи необязательного параметра $charlist. Он представляет собой строку, в которой перечислены все символы, подлежащие удалению.

Например, вызов trim(" test\n ") вернет строку "test".

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

string ltrim(string $st [,string $charlist])

То же, что и trim(), только удаляет исключительно ведущие пробелы, а концевые не трогает. Используется гораздо реже. Старайтесь всегда вместо нее применять trim(), и не прогадаете.

string chop(string $st [,string $charlist])

Удаляет только концевые пробелы, ведущие не трогает. Эта функция будет наверняка очень популярной у тех, кто раньше программировал на Perl. Однако следует заметить, что в PHP она выполняет другую функцию.

Другое имя этой же функции — rtrim().

Базовые функции

int strlen(string $st)

Одна из наиболее полезных функций. Возвращает просто длину строки, т. е. количество содержащихся в $st символов. Как уже упоминалось, строка может содержать любые символы, в том числе и с нулевым кодом (что запрещено в C). Функция strlen() будет правильно работать и с такими строками.

int strpos(string $where, string $what, int $from=0)

Пытается найти в строке $where подстроку (т. е. последовательность символов) $what и в случае успеха возвращает позицию (индекс) этой подстроки в строке. Первый символ строки, как и в C, имеет индекс 0. Необязательный параметр $from можно задавать, если поиск нужно вести не с начала строки $where, а с какой-то другой

Глава 15. Строковые функции

269

позиции. В этом случае следует позицию передать в $from. Если подстроку найти не удалось, функция возвращает false. Однако будьте внимательны, проверяя результат вызова strpos() на false — используйте для этого только оператор === (выше было описано, почему).

int strrpos(string $where, char $what)

Данная функция похожа strpos(), но несет несколько иную нагрузку. Она ищет в строке $where последнюю позицию, в которой встречается подстрока $what.

Замечание касается PHP 4, но не PHP 5. Если $what — строка из нескольких символов, то выявляется только первый из них, остальные не играют никакой роли!

В случае, если совпадение не найдено, возвращается false (см. замечание по этому поводу для strpos()).

int strcmp(string $str1, string $str2)

Сравнивает две строки посимвольно (точнее, побайтово) и возвращает: 0, если строки полностью совпадают; 1, если строка $str1 лексикографически меньше $str2; 1, если, наоборот, $str1 "больше" $str2. Так как сравнение идет побайтово, то регистр символов влияет на результаты сравнений.

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

int strcasecmp(string $str1, string $str2)

То же самое, что и strcmp(), только при работе не учитывается регистр букв. Например, с точки зрения этой функции "ab" и "AB" равны.

Если ваша строка состоит только из "английских" букв, проблем не будет. Однако в случае использования "русских" букв результат (точнее, правильность) работы функции strcasecmp() сильно зависит от настроек текущей локали (см. разд. "Установка локали (локальных настроек)" далее в этой главе).

Работа с подстроками

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

string substr(string $str, int $start [,int $length])

Данная функция тоже применяется очень часто. Ее назначение — возвращать участок строки $str, начиная с позиции $start и длиной $length. Если $length не задана, то подразумевается подстрока от $start до конца строки $str. Если $start боль-

270

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

ше, чем длина строки, или же значение $length равно нулю, то возвращается пустая подстрока.

Однако эта функция может делать и еще довольно полезные вещи. К примеру, если мы передадим в $start отрицательное число, то будет считаться, что это число является индексом подстроки, но только отсчитываемым от конца $str (например, 1 означает "начиная с последнего символа строки"). Параметр $length, если он задан, тоже может быть отрицательным. В этом случае последним символом возвращенной подстроки будет символ из $str с индексом $length, определяемым от конца строки.

Замена

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

string str_replace(string $from, string $to, mixed $text)

Заменяет в строке $text все вхождения подстроки $from (с учетом регистра) на $to и возвращает результат. Исходная строка, переданная третьим параметром, при этом не меняется. Эта функция работает значительно быстрее, чем более универсальные ereg_replace() и preg_replace(), которые мы рассмотрим в гл. 24, и ее часто используют, если нет необходимости в каких-то экзотических правилах поиска подстроки. Например, вот так мы можем заместить все символы перевода строки на их HTMLэквивалент — тег <br>:

$st = str_replace("\n", "<br>\n", $st)

То, что в строке <br>\n тоже есть символ перевода строки, никак не влияет на работу функции, т. е. функция производит лишь однократный проход по строке. Для решения описанной задачи также применима функция nl2br(), которая работает чуть быстрее.

Обратите внимание, что параметр $text описан выше как mixed. Дело в том, что допустимо передавать вместо него целый массив строк, а не только одну-един- ственную строку. Если $text — массив, то замена производится в каждом его элементе, а возвращает функция результирующий список.

string str_ireplace(string $from, string $to, string $text)

Данная функция появилась лишь к PHP 5. Она работает так же, как str_replace(), но только заменяет строки без учета регистра символов.

string substr_replace(string $text, string $to, int $start [,int $len])

Функция предназначена для замены в строке $text участка, начинающегося с позиции $start и длины $len. Этот участок заменяется на значение параметра $to.

На параметры $start и $len накладываются те же ограничения и разрешения, что и на аргументы функции substr().

Глава 15. Строковые функции

271

Конечно, в большинстве случаев вызов substr_replace($text, $to, $start, $len) эквивалентен следующему выражению:

substr($text, 0, $start) . $to . substr($text, $start+$len)

Однако substr_replace() работает быстрее, да и записывается короче.

Подстановка

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

string str_replace(list $from, list $to, mixed $text)

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

Если $from и $to — массивы, замена происходит так: каждый элемент из $from заменяется на соответствующий (по номеру) элемент из $to. Таким образом, можно за один вызов функции заменять сразу несколько пар подстрок (листинг 15.3).

Листинг 15.3. Файл str_replace.php

<?php ## Множественная замена в строке. $from = array("{TITLE}", "{BODY}"); $to = array(

"Философия",

"Представляется логичным, что сомнение иллюстрирует онтологический смысл жизни. Отношение к современности поразительно."

);

echo str_replace($from, $to, " <title>{TITLE}</title> <body>{BODY}</body>

");

?>

string strtr(string $str, string $from, string $to)

Эта функция применяется не столь широко, но все-таки иногда бывает довольно полезной. Делает она следующее: в строке $str заменяет все символы, встречающиеся в $from, на их "парные" (т. е. расположенные в тех же позициях, что и во $from) из $to.

string strtr(string $str, array $substitutes)

Как видите, у функции strtr() существует две разновидности: первая — с тремя параметрами (рассмотрена выше), и вторая — с двумя. Рассмотрим подробно второй вариант.

Функция strtr() с двумя параметрами берет строку $str и проводит в ней контекстный поиск и замену: ищутся подстроки — ключи в массиве $substitutes — и заме-

272

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

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

Функцию strtr() удобно использовать для перевода русского текста в так называемый "транслит". В транслите слово "машина" выглядит так: "mashina". Листинг 15.4 иллюстрирует использование strtr() для транслитерации текста. Обратите внимание, что мы используем сразу оба варианта функции: с двумя и с тремя аргументами.

Листинг 15.4. Файл translit.php

<?php ## Транслитерация строк. function transliterate($st) {

$st = strtr($st,

"абвгдежзийклмнопрстуфыэАБВГДЕЖЗИЙКЛМНОПРСТУФЫЭ",

"abvgdegziyklmnoprstufieABVGDEGZIYKLMNOPRSTUFIE"

);

 

 

 

$st = strtr($st, array(

 

 

'ё'=>"yo",

'х'=>"h",

'ц'=>"ts",

'ч'=>"ch", 'ш'=>"sh",

'щ'=>"shch",

'ъ'=>'',

'ь'=>'',

'ю'=>"yu", 'я'=>"ya",

'Ё'=>"Yo",

'Х'=>"H",

'Ц'=>"Ts",

'Ч'=>"Ch", 'Ш'=>"Sh",

'Щ'=>"Shch",

'Ъ'=>'',

'Ь'=>'',

'Ю'=>"Yu", 'Я'=>"Ya",

));

 

 

 

return $st;

 

 

 

}

echo transliterate("У попа была собака, он ее любил."); ?>

Результатом работы этой программы будет строка: "U popa bila sobaka, on ee lyubil.".

Существует стандарт на транслитерацию текста, применяемый, например, при выдаче загранпаспортов. Функция, описанная в листинге 15.4, этому стандарту не следует. Зато она порождает значительно более приятный для глаз результат.

Что же лучше — str_replace() или strtr()? Функция strtr() начинает поиск с самой длинной подстроки и не проходит по одному и тому же ключу дважды. Рассмотрим листинг 15.5. Мы хотели поменять местами два слова — "matrix" и "you" — в строке текста "matrix has you" ("ты в матрице").

Листинг 15.5. Файл replace.php

<?php ## Различия между strtr() и str_replace(). $text = "matrix has you";

$repl = array("matrix"=>"you", "you"=>"matrix"); echo "str_replace(): ".

str_replace(array_keys($repl), array_values($repl), $text)."<br>";

Глава 15. Строковые функции

273

echo "strtr(): ".

strtr($text, $repl);

?>

Функции array_keys() и array_values(), которые мы рассмотрим в следующей главе, возвращают, соответственно, все ключи и все значения в массиве (в виде обычных списков).

Запустив данную программу, мы увидим, что функции strtr() и str_replace() дают разные результаты!

str_replace(): matrix has matrix

strtr(): you has matrix

Очевидно, мы рассчитывали на второй вариант ("you has matrix" — примерно "матрица в тебе"), поэтому использование strtr() для нас предпочтительнее.

Почему так происходит? Давайте посмотрим. Функция str_replace() в цикле перебирает свой первый аргумент-массив и заменяет найденные подстроки:

1.На первой итерации заменяется "matrix"=>"you". Текст принимает вид: "you has you" (примерно "ты сам в себе").

2.На второй итерации заменяется "you"=>"matrix". Текст принимает вид: "matrix has matrix" ("матрица в матрице" — абсурд!). Функция не знает, что первое "you" заменять не надо, потому что оно было получено на предыдущей итерации.

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

Алгоритмически это реализуется так: в цикле по каждой позиции в строке (а не по каждой паре замены!) ищется, нет ли в массиве ключа максимальной длины, который "укладывается" в текущую позицию строки. Если такой ключ есть, то производится замена, и поиск продолжается в оставшейся части строки. За то, что strtr() работает именно так, приходится расплачиваться: если ключи массива замен длинные (а точнее, сильно различаются по длине между собой), замена происходит очень медленно — гораздо медленнее, чем при использовании str_replace().

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

Преобразования символов

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

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.