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

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

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

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)

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

251

Читает из файла $f $numbytes символов и возвращает строку этих символов. После чтения указатель файла продвигается к следующим после прочитанного блока позициям (это происходит и для всех остальных функций, так что дальше я буду пропускать такие подробности). Разумеется, если $numbytes больше, чем можно прочитать из файла (например, раньше достигается конец файла), возвращается то, что удалось считать. Этот прием можно использовать, если вам нужно считать в строку файл целиком. Для этого просто задайте в $numbytes очень большое число (например, сто тысяч). Но если вы заботитесь об экономии памяти в системе, так поступать не рекомендуется. Дело в том, что в некоторых версиях PHP передача большой длины строки во втором параметре fread() вызывает первоначальное выделение этой памяти в соответствии с запросом (даже если строка гораздо короче). Конечно, потом лишняя память освобождается, но все же ее может и не хватить для начального выделения.

int fwrite(int $f, string $st)

Записывает в файл $f все содержимое строки $st. Эта функция составляет пару для fread(), действуя "в обратном направлении".

Например, с помощью описанных двух функций можно копировать файлы (правда, в PHP есть для этого отдельная функция copy()), считав файл

целиком посредством fread() и затем записав в новое место при помощи fwrite().

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

Построчные чтение/запись

string fgets(int $f, int $length)

Читает из файла одну строку, заканчивающуюся символом новой строки \n. Этот символ также считывается и включается в результат. Если строка в файле занимает больше $length-1 байтов, то возвращаются только ее $length-1 символов. Функция полезна, если вы открыли файл и хотите "пройтись" по всем его строкам. Однако даже в этом случае лучше (и быстрее) будет воспользоваться функцией File(), которая рассматривается ниже. Стоит также заметить, что эта функция (ровно как и функция fread()) в случае текстового режима) в Windows заботится о преобразовании пар \r\n в один символ \n, так что будьте внимательны при работе с текстовыми файлами в этой операционной системе.

int fputs(int $f, string $st)

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

252

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

функции являются синонимами. Что ж... Несмотря на это, я все-таки рискну выдвинуть такое предположение.

Чтение CSV-файла

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

list fgetcsv(int $f, int $length, char $delim=’,’)

Функция читает одну строку из файла, заданного дескриптором $f, и разбивает ее по символу $delim. Параметр $delim должен обязательно быть строкой из одного символа, в противном случае принимается во внимание только первый символ этой строки. Функция возвращает получившийся список или false, если строки кончились. Параметр $length задает максимальную длину строки точно так же, как это делается в fgets(). Пустые строки в файле не игнорируются, а возвращаются как список из одного элемента — пустой строки.

Функция fgetcsv() работает чуть быстрее пары fgets()/explode(), но зато она, как мы можем видеть, гораздо менее универсальна. Применяйте ее в таком контексте:

$f=fopen("file.csv","r") or die("Ошибка!"); for($i=0; $data=fgetcsv($f, 1000, ";"); $i++) {

$num = count($data);

if($num==1 && $data[0]==="") continue;

echo "<h3>Строка номер $i ($num полей):</h3>"; for($c=0; $c<$num; $c++)

print "[$c]: $data[$c]<br>";

}

fclose($f);

Положение указателя текущей позиции

int feof(int $f)

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

контексте: $f=fopen("myfile.txt","r");

while(!feof($f))

{$st=fgets($f);

//теперь мы обрабатываем очередную строку $st

//. . .

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

253

}

fclose($f);

Лучше избегать подобных конструкций, т. к. в случае больших файлов они довольно медлительны. Лучше читайте файл целиком при помощи File() (см. ниже) или fread() — конечно, если вам нужен доступ к каждой строке этого файла, а не только к нескольким первым!

int fseek(int $f, in $offset, int $whence=SEEK_SET)

Устанавливает указатель файла на байт со смещением $offset (от начала файла, от его конца или от текущей позиции, в зависимости от параметра $whence). Это, впрочем, может и не сработать, если дескриптор $f ассоциирован не с обычным локальным файлом, а с соединением HTTP или FTP.

Параметр $whence, как уже упоминалось, задает, с какого места отсчитывается смещение $offset. В PHP для этого существуют три константы, равные, соответствен-

но, 0, 1 и 2:

rSEEK_SET — устанавливает позицию начиная с начала файла;

rSEEK_CUR — отсчитывает позицию относительно текущей позиции;

rSEEK_END — отсчитывает позицию относительно конца файла.

В случае использования последних двух констант параметр $offset вполне может быть отрицательным (а при применении SEEK_END он будет отрицательным наверня-

ка).

Как это ни странно, но в случае успешного завершения эта функция возвращает 0, а в случае неудачи −1. Почему так сделано — неясно. Наверное, по аналогии с ее Сиэквивалентом?

int ftell(int $f)

Возвращает положение указателя файла. Собственно, вот и все, что делает эта функция.

Функции для определения типов файлов

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

Определение типа файла

bool file_exists(string $filename)

254

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

Возвращает true, если файл с именем $filename существует на момент вызова. Используйте эту функцию с осторожностью! Например, следующий код никуда не годится с точки зрения безопасности:

$fname="/etc/passwd"; if(!file_exists($fname) $f=fopen($fname,"w");

else $f=fopen($fname,"r");

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

string filetype(string $filename)

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

rfile — обычный файл;

rdir — каталог;

rlink — символическая ссылка;

rfifo — fifo-канал;

rblock — блочно-ориентированное устройство;

rchar — символьно-ориентированное устройство;

runknown — неизвестный тип файла.

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

bool is_file(string $filename)

Возвращает true, если $filename — обычный файл. bool is_dir(string $filename)

Возвращает true, если $filename — каталог. bool is_link(string $filename)

Возвращает true, если $filename — символическая ссылка.

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

255

Определение возможности доступа

В PHP есть еще несколько функций, начинающихся с префикса is_. Они довольно интеллектуальны, поэтому рекомендуется использовать их перед "опасными" открытиями файлов.

bool is_readable(string $filename)

Возвращает true, если файл может быть открыт для чтения. bool is_writeable(string $filename)

Возвращает true, если в файл можно писать. bool is_executable(string $filename)

Возвращает true, если файл — исполняемый.

Определение параметров файла

array stat(string $filename)

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

0— устройство;

1— номер узла inode;

2— атрибуты защиты файла;

3— число синонимов ("жестких" ссылок) файла;

4— идентификатор uid владельца;

5— идентификатор gid группы;

6— тип устройства;

7— размер файла в байтах;

8— время последнего доступа в секундах, прошедших с 1 января 1970 года;

9— время последней модификации содержимого файла;

10— время последнего изменения атрибутов файла;

11— размер блока;

12— число занятых блоков.

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

Если $filename задает не имя файла, а имя символической ссылки, то все-таки будет возвращена информация о том файле, на который ссылается эта ссылка (а не о

256

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

ссылке). Для получения информации о ссылке можно воспользоваться вызовом lstat(), имеющим точно такой же синтаксис, что и stat().

Специализированные функции

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

int fileatime(string $filename)

Возвращает время последнего доступа (access) к файлу (например, на чтение). Время выражается в количестве секунд, прошедших с 1 января 1970 го-да. Если файл не обнаружен, возвращает false.

int filemtime(string $filename)

Возвращает время последнего изменения файла или false в случае отсутствия файла.

int filectime(string $filename)

Возвращает время создания файла.

int filesize(string $filename)

Возвращает размер файла в байтах или false, если файла не существует.

int touch(string $filename [, int $timestamp])

Устанавливает время модификации указанного файла $filename равным $timestamp (в секундах, прошедших с 1 января 1970 года). Если второй параметр не указан, то подразумевается текущее время. В случае ошибки возвращается false.

Если файла с указанным именем не существует, он создается пустым.

Функции для работы с именами файлов

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

string basename(string $path)

Выделяет основное имя файла из пути $path. Вот несколько примеров:

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

257

echo basename("/home/somebody/somefile.txt"); // выводит "somefile.txt"

echo basename("/");

// ничего не выводит

echo basename("/.");

// выводит "."

echo basename("/./");

// также выводит "."

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

Windows.

string dirname(string $path)

Возвращает имя каталога, выделенное из пути $path. Функция довольно "разумна" и умеет обрабатывать нетривиальные ситуации, как это явствует из примеров:

echo dirname("/home/file.txt"); // выводит "/home" echo dirname("../file.txt"); // выводит ".."

echo dirname("/file.txt");

// выводит "/" под Unix, "\" под Windows

echo dirname("/");

// то же самое

echo dirname("file.txt");

// выводит "."

Заметьте, что если функции dirname() передать "чистое" имя файла, она вернет ".", что означает "текущий каталог".

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

string tempnam(string $dir, string $prefix)

Генерирует имя файла в каталоге $dir с префиксом $prefix в имени, причем так, чтобы созданный под этим именем в будущем файл был уникален. Для этого к строке $prefix присоединяется некое случайное число. Например, вызов tempnam("/tmp","temp") может возвратить что-то типа /tmp/temp3a6b243c.

Если такое имя нужно создать в текущем каталоге, передайте, как обычно, $dir=".". Обратите внимание, что использовать tempnam() в следующем контексте опасно:

$fname=tempnam(); $f=fopen($fname,"w");

// работаем с временным файлом

Дело в том, что хотя функция и возвращает уникальное имя, все-таки существует вероятность того, что между tempnam() и fopen() сюда "вклинится" какой-нибудь другой процесс, в котором функция tempnam() сгенерировала идентичное имя файла. Такая вероятность исчезающе мала, но все-таки она существует. Поэтому лучше воспользоваться функцией tmpfile() или функциями блокировки.