Самоучитель PHP 4 - Котеров Д. В
..pdf248 |
Часть 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() или функциями блокировки.