Самоучитель по PHP 4
.pdfГлава 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(), которая устанавливает так называемую "рекомендательную блокировку" для файла. Это означает, что блокирование доступа