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

Самоучитель PHP 4 - Котеров Д. В

..pdf
Скачиваний:
93
Добавлен:
24.05.2014
Размер:
4.38 Mб
Скачать

Глава 14

Математические

функции

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

Встроенные константы

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

 

Таблица 14.1. Математические константы.

 

 

 

Константа

Величина

Пояснение

 

 

 

M_PI

3,14159265358979323846

Число π

M_E

2,7182818284590452354

e

M_LOG2E

1,4426950408889634074

Log2(e)

M_LOG10E

0,43429448190325182765

Lg(e)

M_LN2

0,69314718055994530942

Ln(2)

M_LN10

2,30258509299404568402

Ln(10)

M_PI_2

1,57079632679489661923

π /2

M_PI_4

0,78539816339744830962

π /4

M_1_PI

0,31830988618379067154

1/ π

M_2_PI

0,63661977236758134308

2/ π

M_SQRTPI

1,77245385090551602729

sqrt(π)

M_2_SQRTPI

1,12837916709551257390

2/sqrt(π)

 

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

239

 

M_SQRT2

1,41421356237309504880

sqrt(2)

 

 

 

Таблица 14.1 (окончание)

 

 

 

 

 

 

Константа

Величина

Пояснение

 

 

 

 

 

 

M_SQRT3

1,73205080756887729352

sqrt(3)

 

M_SQRT1_2

0,70710678118654752440

1/sqrt(2)

 

M_LNPI

1,14472988584940017414

Ln(π)

 

M_EULER

0,57721566490153286061

Постоянная Эйлера

Надо заметить, разработчики PHP что-то слишком разошлись, когда вводили стандартные константы. Например, я не могу даже и представить, зачем в Webпрограммировании может потребоваться, например, константа Эйлера. Что же, это их право….

Функции округления

mixed abs(mixed $number)

Возвращает модуль числа. Тип параметра $number может быть float или int, а тип возвращаемого значения всегда совпадает с типом этого параметра.

double round(double $val)

Округляет $val до ближайшего целого и возвращает результат, например: $foo = round(3.4); // $foo == 3.0

$foo = round(3.5); // $foo == 4.0 $foo = round(3.6); // $foo == 4.0

int ceil(float $number)

Возвращает наименьшее целое число, не меньшее $number. Разумеется, передавать в $number целое число бессмысленно.

int floor(float $number)

Возвращает максимальное целое число, не превосходящее $number.

Случайные числа

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

240

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

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

int mt_rand(int $min=0, int $max=RAND_MAX)

Функция возвращает случайное число, достаточно равномерно даже для того, чтобы использовать ее в криптографии. Подробнее о том алгоритме, который она использует, можно прочитать в Интернете по адресу http://www.math.keio.ac.jp/~matumoto/emt.html, а исходные тексты найти по адресу http://www.scp.syr.edu/~marc/hawk/twister.html. Если вы хотите генерировать числа не от 0 до RAND_MAX (эта константа задает максимально допустимое случайное число, и ее можно получить при помощи вызова mt_getrandmax()), задайте соответствующий интервал в параметрах $min и $max.

Не забудьте только перед первым вызовом этой функции запустить mt_srand().

Давайте теперь рассмотрим один из случаев применения функции mt_rand(). Речь пойдет об извлечении строки со случайным номером из текстового файла (работу с файлами мы рассмотрим чуть позже, а пока скажу лишь, что функция fget() читает очередную строку из файла, дескриптор которого указан ей в первом параметре, а второй параметр задает максимально возможную длину этой строки, для нас это — очень большое число). Поступим так:

for($i=0; mt_rand(0,$i)<1; $i++) $s=fgets($OurFile,10000);

echo "Случайная строка: $s";

Этот способ работает в строгом соответствии с теорией вероятностей: для первой строки вероятность ее извлечения будет 100%, для второй — 50% (она перепишется поверх первой), для третьей — 33%, и т. д. Например, если файл состоит всего из трех строк, то вероятность извлечения третьей строки, как мы уже заметили, будет равна 33%, а значит, первой или второй — соответственно, 66%. Но вероятность извлечения второй строки после первой равна 50%, а 50% от 66% будет также 33%, т. е. вероятность извлечения каждой строки одинакова. Мы видим, что для файла из трех строк алгоритм работает правильно. Не вдаваясь в математические подробности, скажу, что он работает верно и для любого количества строк.

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

Глава 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