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

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

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

Глава 14. Математические функции

241

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

void mt_srand(int $seed)

Настраивает генератор случайных чисел на новую последовательность. Дело в том, что хотя числа, генерируемые mt_rand(), достаточно равновероятны, но у них есть один недостаток (который, как это обычно бывает, иногда перерастает в достоинство): последовательность сгенерированных чисел будет одинакова если сценарий вызвать несколько раз подряд. Функция mt_srand() как раз решает данную проблему: она выбирает новую последовательность на основе параметра $seed, причем практически непредсказуемым образом. Чаще всего ее инициализируют так:

mt_srand(time()+(double)microtime()*1000000); $randval = mt_rand();

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

int mt_getrandmax()

Возвращает максимальное число, которое может быть сгенерировано функцией mt_rand() — иными словами, константу RAND_MAX.

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

string base_convert(string $number, int $frombase, int $tobase)

Переводит число $number (заданное как строка в системе счисления по основанию $frombase) в систему по основанию $tobase. Параметры $frombase и $tobase

могут принимать значения только от 2 до 36 включительно. В строке $number цифры обозначают сами себя, буква a соответствует 11, b — 12, и т. д. до z, которая обозначает 36. Например, следующие команды выведут 11111111 (8 единичек), потому что это — не что иное, как представление шестнадцатеричного числа FF в двоичной системе счисления:

echo base_convert("FF",16,2);

int bindec(string $binary_string)

242

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

Преобразует двоичное число, заданное в строке $binary_string, в десятичное число.

string decbin(int $number)

Возвращает строку, представляющую собой двоичное представление целого числа $number. Максимальное число, которое еще может быть преобразовано, равно 2 147 483 647, которое выглядит как 31 единичка в двоичной системе.

Существуют аналогичные функции для восьмеричной и шестнадцатеричной систем. Называются они так же, только вместо "bin" подставляется соответственно "oct" и "hex".

Минимум и максимум

mixed min(mixed $arg1 [int $arg2, ..., int $argn])

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

mixed max(mixed $arg1 [int $arg2, ..., int $argn])

Функция работает аналогично min(), только ищет максимальное значение.

Степенные функции

float sqrt(float $arg)

Возвращает квадратный корень из аргумента. Если аргумент отрицателен, то генерируется предупреждение, но работа программы не прекращается! Это выглядит довольно странно: интересно, что в этом случае возвращается функцией?..

float log(float $arg)

Возвращает натуральный логарифм аргумента. В случае недопустимого числа печатает предупреждение, но, как и sqrt(), не завершает программу.

float exp(float $arg)

Возвращает e (2,718281828…) в степени $arg.

float pow(float $base, float $exp)

Возвращает $base в степени $exp.

Глава 14. Математические функции

243

Тригонометрия

Далее рассмотрим тригонометрические функции. Правда, они редко применяются

при программировании сценариев, но все же...

float acos(float $arg)

Возвращает арккосинус аргумента.

float asin(float $arg)

Возвращает арксинус.

float atan(float $arg)

Возвращает арктангенс аргумента.

float atan2(float $y, float $x)

Возвращает арктангенс величины $y/$x, но с учетом той четверти, в которой лежит точка ($x, $y). Эта функция возвращает результат в радианах, принадлежащий отрезку от ? до ?. Вот пара примеров:

$alpha=atan2(1,1); // $alpha==pi/4 $alpha=atan2(-1,-1); // $alpha==-3*pi/4

float sin(float arg)

Возвращает синус аргумента. Аргумент задается в радианах.

float cos(float $arg)

Возвращает косинус аргумента.

float tan(float arg)

Возвращает тангенс аргумента, заданного в радианах.

double pi()

Возвращает число ?. Эту функцию в PHP версии 4 обязательно нужно вызывать с парой пустых скобок (в отличие от PHP 3):

echo pi()*10;

Впрочем, наверное, лучше будет воспользоваться константой M_PI?..

Глава 15

Работа с файлами

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

Во-вторых, вы теперь можете работать с файлами на удаленных серверах Web в точности так же, как и со своими собственными (ну, разве что записывать в них можно не всегда). Если вы предваряете имя файла строкой http:// или ftp://, то PHP понимает, что нужно на самом деле установить сетевое соединение и работать именно с ним, а не с файлом. При этом в программе такой файл ничем не отличается от обычного (если у вас есть соответствующие права, что вы можете и записывать в подобный HTTPили FTP-файл).

О текстовых и бинарных файлах

Во многих (да что там — практически во всех) языках программирования для работы с текстовыми файлами применяется некоторый трюк. Вот в чем он заключается. Не секрет, что в Unix-системах для отделения одной строки файла от другой используется один специальный символ — его принято обозначать \n.

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

В Windows по историческим причинам для разделения строк применяется не один, а сразу два символа, следующих подряд, — \r\n. Для того чтобы языки программирования были лучше переносимы с одной операционной системы на другую, при чтении текстовых файлов эта комбинация \r\n преобразуется "на лету" в один символ \n, так что программа и не замечает, что формат файла не такой, как в Unix. В результате этой деятельности, если мы, например, прочитаем содержимое всего текстового

Глава 15. Работа с файлами

245

файла в строку, то длина такой строки наверняка окажется меньше физического размера файла — ведь из нее "съелись" некоторые символы \r. Это относится к системе Windows и MacOS (кстати, в последней применяется комбинация не \r\n, а наоборот — \n\r, что довольно-таки забавно). При записи строки в текстовый файл происходит в точности наоборот: один \n становится на диске парой \r\n.

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

Так как PHP был написан целиком на Си, а Си использует трансляцию символов перевода строк, то описанная техника работает и в PHP. Однако тут есть один очень опасный момент. Дело в том, что разработчики PHP в официальной документации к функции fopen() старательно умалчивают о том, что интерпретатор может работать с файлами в режиме трансляции символа перевода строки. Так вот, я возьму на себя смелость заявить, что такая возможность в действительности существует, а тесты подтвердили, что ее можно корректно использовать как в Windows и MacOS, так и в Unix. Подробнее об этом мы поговорим при рассмотрении функции fopen().

Если файл открыт в режиме бинарного чтения/записи, то PHP совершенно все равно, что вы читаете или пишете. Вы можете совершенно спокойно считать содержимое какого-нибудь бинарного файла (например, GIF-рисунка) в обычную строковую переменную, а потом записать эту строку в другой файл, и при этом информация нисколько не исказится. Правда, при чтении текстового файла в Windows вы получите символы \r\n в конце строки вместо одного \n, если не предпримете некоторых действий, а откроете файл, как об этом написано в документации. Об этом речь ниже.

Открытие файла

Как и в Си, работа с файлами в PHP разделяется на три этапа. Сначала файл открывается в нужном режиме, при этом возвращается некое целое число, служащее идентификатором открытого файла (дескриптор файла). Затем настает очередь команд работы с файлом (чтение или запись, или и то и другое), причем они "привязаны" уже к дескриптору файла, а не к его имени. После этого файл лучше всего закрыть (хотя это можно и не делать, поскольку PHP автоматически закрывает все файлы по завершении сценария).

int fopen(string $filename, string $mode, bool $use_include_path=false)

Открывает файл с именем $filename в режиме $mode и возвращает дескриптор открытого файла. Если операция "провалилась", то, как это принято, fopen() возвращает false. Впрочем, мы можем не особо беспокоиться, проверяя выходное значе-

246

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

ние на ложность — вполне подойдет и проверка на ноль, потому что дескриптор 0 в системе соответствует стандартному потоку ввода, а он, очевидно, никогда не будет открыт функцией fopen() (во всяком случае, пока не будет закрыт нулевой дескриптор, а это делается крайне редко). Необязательный параметр $use_include_path говорит PHP о том, что, если задано относительное имя файла, его следует искать также и в списке путей, используемом инструкциями include и require. Обычно этот параметр не используют.

Параметр $mode может принимать следующие значения:

rr — файл открывается только для чтения. Если файла не существует, вызов регистрирует ошибку. После удачного открытия указатель файла устанавливается на его первый байт, т. е. на начало;

rr+ — файл открывается одновременно на чтение и запись. Указатель текущей позиции устанавливается на его первый байт. Как и для режима r, если файла не существует, возвращается false. Следует отметить, что если в момент записи указатель файла установлен где-то в середине файла, то данные запишутся прямо поверх уже имеющихся, а не "раздвинут" их, при необходимости увеличив размер файла. Будьте внимательны;

rw — создает новый пустой файл. Если на момент вызова уже был файл с таким именем, то он предварительно уничтожается. В случае неверно заданного имени файла вызов, как нетрудно догадаться, "проваливается";

rw+ — аналогичен r+, но если файла изначально не существовало, создает его. После этого с файлом можно работать как в режиме чтения, так и записи. Если файл существовал до момента вызова, его содержимое удаляется;

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

ra+ — открывает файл в режиме чтения и записи, указатель файла устанавливается на конец файла, при этом содержимое файла не уничтожается. Отличается от a тем, что если файла изначально не существовало, то он создается. Этот режим полезен, если вам нужно что-то дописать в файл (например, в журнал), но вы не знаете, создан ли уже такой файл;

Но это еще не полное описание параметра $mode. Дело в том, что в конце любой из строк r, w, a, r+, w+ и a+ может находиться еще один необязательный символ — b или t. Если указан b (или не указан вообще никакой), то файл открывается в режиме бинарного чтения/записи. Если же это t, то для файла устанавливается режим трансляции символа перевода строки, т. е. он воспринимается как текстовый.

Глава 15. Работа с файлами

247

О режиме t нет ни слова в документации PHP (во всяком случае, на момент написания этих строк), однако, как показывают мои тесты, он работает на всех системах.

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

Листинг 15.1. Сценарий test.php: работа с текстовыми файлами

<?

//Получает в параметрах строку и возвращает через пробел коды

//символов, из которых она состоит

function MakeHex($st)

{for($i=0; $i<strlen($st); $i++) $Hex[]=sprintf("%2X",ord($st[$i])); return join(" ",$Hex);

}

// Открываем файл разными способами $f=fopen("test.php","r"); // бинарный режим echo MakeHex(fgets($f,100)),"<br>\n"; $f=fopen("test.php","rt"); // текстовый режим echo MakeHex(fgets($f,100)),"<br>\n";

?>

Первая строчка файла test.php состоит всего из двух символов — это < и ?. За ними должен следовать маркер конца строки. Сценарий показывает, как выглядит этот маркер, т. е. состоит ли он из одного или двух символов.

Запустим этот сценарий в Unix. Мы получим две одинаковые строки, которые выводят операторы echo:

3C 3F 0A

3C 3F 0A

Отсюда следует, что в этой системе физический конец строки обозначается одним символом — кодом 0x0A, или \n (коды 0x3C и 0x3F соответствуют символам < и ?). В то же время, если запустить сценарий в Windows, мы получим такой результат:

3C 3F 0D 0A

3C 3F 0A

248

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

Как видите, бинарное и текстовое чтение дали разные результаты! В последнем случае произошла трансляция маркера конца строки.

Как уже говорилось, можно предварять имя файла строкой http:// или ftp://, при этом прозрачно будет осуществляться доступ к файлу с удаленного хоста.

В случае HTTP-доступа PHP открывает соединение с указанным сервером, а также посылает ему нужные заголовки: Host и GET. После чего при помощи файлового дескриптора из удаленного файла можно читать обычным образом — например, посредством все той же функции fgets().

Если же вы открываете FTP-файл, то в него можно производить либо запись, либо читать из него, но не и то и другое одновременно. Кроме того, FTP-сервер должен поддерживать пассивный режим передачи (впрочем, большинство серверов его поддерживают). Не забудьте также указать логин и пароль, как это сделано в примерах ниже.

Дам небольшой совет: не используйте обратные слэши \ в именах файлов, как это принято в DOS и Windows. Просто забудьте про этот архаизм. Поможет вам в этом PHP, который незаметно в нужный момент переводит прямые слэши / в обратные (разумеется, если вы работаете под Windows). Если же вы все-таки не можете обойтись без обратного слэша, не забудьте его удвоить, потому что в строках он воспринимается как спецсимвол:

$fp = fopen ("c:\\windows\\hosts", "r");

Еще раз предупреждаю: этот способ не переносим между операционными системами и из рук вон плох. Не используйте его!

Вот несколько примеров:

// Открывает файл на чтение

$f = fopen("/home/user/file.txt", "r") or die("Ошибка!"); // Открывает HTTP-соединение на чтение

$f = fopen("http://www.php.net/", "r") or die("Ошибка!");

// Открывает FTP-соединение с указанием логина и пароля для записи

$f = fopen("ftp://user:password@example.com/", "w") or die("Ошибка!");

Конструкция or die()

Давайте еще раз посмотрим на предыдущие примеры. Обратите внимание на доселе не встречавшуюся нам конструкцию or die(). Ее особенно удобно применять как раз при работе с файлами. Как мы знаем, оператор or имеет очень низкий приоритет (даже ниже, чем у =), поэтому в нашем примере всегда выполняется уже после присваивания. Иными словами, первая строчка примера с точки зрения PHP выглядит так:

($f=fopen("/home/user/file.txt", "r")) or die("Ошибка!");

Глава 15. Работа с файлами

249

Конечно, то, что or обозначает "логическое ИЛИ" в нашем случае не так интересно (ибо возвращаемое значение просто игнорируется). Нас же сейчас интересует другое свойство оператора: выполнять второй свой операнд только в случае ложности первого. Смотрите: если файл открыть не удалось, fopen() возвращает false, а значит, осуществляется вызов die() "на другом конце" оператора or.

Заметьте, что нельзя просто так заменить or на, казалось бы равнозначный ему оператор ||, потому что последний имеет гораздо более высокий приоритет — выше, чем у =. Таким образом, в результате вызова функции

$f=fopen("/home/user/file.txt", "r") || die("Ошибка!");

в действительности будет выполнено

$f = (fopen("/home/user/file.txt", "r") || die("Ошибка!"));

Как видите, это не совсем то, что нам нужно.

Безымянные временные файлы

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

int tmpfile()

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

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

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

250

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

Закрытие файла

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

int fclose(int $fp)

Закрывает файл, открытый предварительно функцией fopen() (или popen() или fsockopen(), но об этом позже). Возвращает false, если файл закрыть не удалось (например, что-то с ним случилось или же разорвалась связь с удаленным хостом). В противном случае возвращает значение "истина".

Заметьте, что вы должны всегда закрывать FTP- и HTTP-соединения, потому что в противном случае "беспризорный" файл приведет к неоправданному простою канала и излишней загрузке сервера. Кроме того, успешно закрыв соединение, вы будете уверены в том, что все данные были доставлены без ошибок.

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

Чтение и запись

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

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

Блочные чтение/запись

string fread(int $f, int $numbytes)