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

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

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

Глава 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() или функциями блокировки.

258

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

string realpath(string $path)

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

echo realpath("../t.php"); // абсолютный путь — например, /home/test.php

echo realpath(".");

// выводит имя текущего каталога

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

Файл, который указывается в параметре $path, должен существовать, иначе функция возвращает false.

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

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

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

bool copy(string $src, string $dst)

Копирует файл с именем $src в файл с именем $dst. При этом, если файл $dst на момент вызова существовал, осуществляется его перезапись. Функция возвращает true, если копирование прошло успешно, а в случае провала — false.

bool rename(string $oldname, string $newname)

Переименовывает (или перемещает, что одно и то же) файл с именем $oldname в файл с именем $newname. Если файл $newname уже существует, регистрируется ошибка, и функция возвращает false. То же происходит и при всех прочих неудачах. Если же все прошло успешно, возвращается true.

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

259

Функция не выполняет переименование файла, если его новое имя располо- жено в другой файловой системе (на другой смонтированной системе в Unix или на другом диске в Windows). Так что никогда не используйте rename() для получения загруженного по HTTP файла (о загрузке подробно рассказано в пятой части книги) — ведь временный каталог /tmp вашего хостинг- провайдера скорее всего располагается на отдельном разделе диска.

bool unlink(string $filename)

Удаляет файл с именем $filename. В случае неудачи возвращает false, иначе — true.

На самом-то деле файл удаляется только в том случае, если число "жестких" ссылок на него стало равным 0. Правда, эта схема специфична для Unix- систем.

list File(string $filename)

Считывает файл с именем $filename целиком и возвращает массив-список, каждый элемент которого соответствует строке в прочитанном файле. Функция работает очень быстро — гораздо быстрее, чем если бы мы использовали fopen() и читали файл по одной строке. Неудобство этой функции состоит в том, что символы конца строки (обычно \n), не вырезаются из строк файла, а также не транслируются, как это делается для текстовых файлов.

array get_meta_tags(string $filename, int $use_include_path=false);

Функция открывает файл и ищет в нем все тэги <meta> до тех пор, пока не встретится закрывающий тэг </head>. Если очередной тэг <meta> имеет вид:

<meta name="название" content="содержимое">

то пара название=>содержимое добавляется в результирующий массив, который под конец и возвращается. Функцию удобно использовать для быстрого получения всех метатегов из указанного файла (что работает гораздо быстрее, чем соответствующее использование fopen() и затем чтение и разбор файла по строкам). Если необязательный параметр $use_include_path установлен, то поиск файла осуществляется не только в текущем каталоге, но и во всех тех, которые назначены для поиска инструкциями include и require.

Другие функции

bool ftruncate(int $f, int $newsize)

260

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

Эта функция усекает открытый файл $f до размера $newsize. Разумеется, файл должен быть открыт в режиме, разрешающем запись. Например, следующий код просто очищает весь файл:

ftruncate($f,0); // очистить содержимое файла

void fflush(int $f)

Заставляет PHP немедленно записать на диск все изменения, которые производились до этого с открытым файлом $f. Что это за изменения? Дело в том, что для повышения производительности все операции записи в файл буферизируются: например, вызов fputs($f,"Это строка!") не приводит к непосредственной записи данных на диск — сначала они попадают во внутренний буфер (обычно размером 8K). Как только буфер заполняется, его содержимое отправляется на диск, а сам он очищается, и все повторяется вновь. Особенный выигрыш от буферизации чувствуется в сетевых операциях, когда просто глупо отправлять данные маленькими порциями. Конечно, функция fflush() вызывается неявно и при закрытии файла.

int set_file_buffer(int $f, int $size)

Эта функция устанавливает размер буфера, о котором мы только что говорили, для указанного открытого файла $f. Чаще всего она используется так:

set_file_buffer($f,0);

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

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

Блокирование файла

При интенсивном обмене данными с файлами в мультизадачных операционных системах встает вопрос синхронизации операций чтения/записи между процессами. Например, пусть у нас есть несколько "процессов-писателей" и один "процесс-читатель". Необходимо, чтобы в единицу времени к файлу имел доступ лишь один процессписатель, а остальные на этот момент времени как бы "подвисали", ожидая своей очереди. Это нужно, например, чтобы данные от нескольких процессов не перемешивались в файле, а следовали блок за блоком. Как мы можем этого достигнуть?

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