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

web - tec / PHP 5 для начинающи

.pdf
Скачиваний:
119
Добавлен:
12.06.2015
Размер:
26.79 Mб
Скачать

Файлы и каталоги 303

и возвращает только один символ из связанного с данным дескриптором файла; если функция достигает конца файла, то она возвращает False. По существу, она работает так же, как и fread() ++++++ вызов:

$one_char = fgetc($fp)

эквивалентен вызову:

$one_char = fread($fp,1)

Модифицируем первоначальный счетчик с использованием в нем функции fgetc(). Для этого можно использовать такой код:

<?php //hit_counter03.php

$counter_file = "./count.dat";

if(!($fp = fopen($counter_file, "r"))) die ("Невозможно открыть файл $counter_file.");

do {

$one_char = fgetc($fp); $counter .= $one_char;

} while($one_char); $counter = (int) $counter; fclose($fp);

Чтобы считать все содержимое файла данных, используется цикл while, потому что функция fgetc() считывает из файла только один символ за раз. Кроме того, не+ обходимо знать, когда прекращать чтение, поэтому последний считанный символ за+ писывается в холостую переменную, и когда она будет равна False, цикл чтения можно прекратить. Однако в таком коде есть один дефект. Как только из файла будет считано значение "0" или " ", условие цикла не выполнится и цикл чтения прекра+ тится. Если планируется, что значение счетчика посещений будет превышать 9, то данная особенность может оказаться серьезной проблемой.

Существует другой способ определить момент завершения цикла чтения ++++++ ис+ пользование функции feof().

Функция feof() служит одной простой цели: она возвращает True, когда достига+ ет конца заданного файла (или если возникает ошибка), и False ++++++ в противном слу+ чае. Функция принимает только один аргумент ++++++ дескриптор файла:

feof($fp)

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

<?php //hit_counter04.php

$counter_file = "./count.dat";

if(!($fp = fopen($counter_file, "r"))) die ("Невозможно открыть файл $counter_file."); while(!feof($fp)) $counter .= fgetc($fp);

$counter = (int) $counter; fclose($fp);

$counter++;

echo "Вы – посетитель № $counter.";

$fp = fopen($counter_file, "w"); fwrite($fp, $counter); fclose($fp);

?>

Функция feof() сообщает оператору while, когда необходимо завершить цикл (когда функция fgetc() в процессе посимвольного считывания достигает конца

304 Глава 7

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

Чтение больших файлов с помощью функции fgetc() может отнимать очень много времени, потому что данная функция считывает только один символ за раз. Считывать множество символов позволяет функция fgets(). Она принимает два ар+ гумента, $fp и $length, и возвращает прочитанную из файла, заданного дескрипто+ ром $fp, строку, длина которой не превышает значения $length ($length ++++1). Функция прекращает чтение по одной из трех перечисленных ниже причин:

прочитано заданное количество байтов;

достигнут конец строки;

достигнут конец файла.

Различие между fgets() и fread() заключается в том, что fgets() прекращает чтение после того, как был достигнут конца строки или считано length ++++1 байтов, тогда как функция fread() игнорирует конец строки и считывает $length байтов. Функцию fgets() можно применить в счетчике, используя следующий код:

<?php //hit_counter05.php

$counter_file = "./count.dat";

if(!($fp = fopen($counter_file, "r"))) die ("Невозможно открыть файл $counter_file."); $counter = (int) fgets($fp, 20);

fclose($fp);

...

Сценарий должен считывать из файла данных только одну строку, поэтому вызов fgets() возвращает именно то, что нужно, т.е. последнее значение счетчика.

Тот, кто сталкивался с импортированием и экспортированием данных, знаком с форматом CSV (comma+separated+value ++++++ значения, разделенные запятыми). (CSV даже имеет собственное расширение файлов: .csv.) В CSV+файлах значения данных разделены запятыми, а строковые значения часто заключаются в двойные кавычки между запятыми. Функция fgetcsv() считывает данные из файлов, предполагая, что эти данные отформатированы в правильном CSV+формате, и помещает найденные в строке данные в массив. Естественно, после этого можно легко использовать полу+ ченный массив с данными.

Функции fgetcsv() необходимо передать корректный дескриптор файла ++++++ чис+ ленное значение, которое должно превышать длину каждой строки (включая символы конца строки), а также необязательные разделители данных (по умолчанию запятая) и ограничители данных (по умолчанию двойные кавычки). Следующий фрагмент ко+ да показывает, как можно получить строку значений данных из CSV+файла:

$file_handle = fopen("my_csv_text_file.csv", "r"); $my_data_values_array = fgetcsv($file_handle, 1000, ",");

В результате работы этого кода формируется массив $my_data_values_array; если бы первым значением в строке текста было чье+нибудь имя (например, Боб), то в элемент $my_data_values_array[0] было бы записано значение ‘‘Боб’’. Следует отметить, что пустая строка в текстовом файле интерпретируется не как ошибка, а как одно NULL+поле.

Функция fputs() представляет собой синоним функции fwrite(); две эти функ+ ции идентичны.

Файлы и каталоги 305

Чтение файлов целиком

Следующие функции предоставляют доступ ко всему содержимому файла за один раз:

file()

fpassthru()

readfile()

Функция file() принимает только один аргумент: строку, содержащую имя фай+ ла. Например, вызов

file("/home/chris/myfile.txt")

возвращает все содержимое файла myfile.txt (находящегося в каталоге /home/chris/) в виде массива, каждый элемент которого представляет собой одну строку файла, включая символ конца строки (CRLF на Windows+платформе). Функция не требует указания дескриптора файла, а позволяет задавать имя файла явно; она автоматически открывает файл, считывает из него данные и по завершении чтения закрывает файл.

Рассмотрим file()+версию счетчика посещений:

<?php //hit_counter06.php

$counter_file = "./count.dat"; $lines = file($counter_file); $counter = (int) $lines[0];

$counter++;

echo "Вы – посетитель № $counter.";

if(!($fp = fopen($counter_file, "w"))) die ("Невозможно открыть файл $counter_file."); fwrite($fp, $counter);

fclose($fp); ?>

Все содержимое файла данных считывается в массив $lines, после чего извлекается только первый элемент данного массива (первая строка файла). То же самое можно бы+ ло бы сделать с помощью комбинации функций feof() и fgets(), например, так:

$fp = fopen ("./count.dat", "r");

while (!feof($fp)) $lines[] = fgets($fp, 1024); fclose ($fp);

Однако делать это нет необходимости, потому что функция file() делает все это автоматически. Как и fopen(), функция file() способна открывать файлы на уда+ ленном узле:

$file_lines = file("http://www.whatyoumaycallit.com/index.html"); foreach($file_lines as $line) echo $line;

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

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

306 Глава 7

<?php //hit_counter07.php

$counter_file = "./count.dat";

if(!($fp = fopen($counter_file, "r"))) die ("Невозможно открыть файл $counter_file."); $counter = (int) fread($fp, 20);

fclose($fp);

$counter++;

if(!($fp = fopen($counter_file, "w"))) die ("Невозможно открыть файл $counter_file."); fwrite($fp, $counter);

fclose($fp);

if(!($fp = fopen($counter_file, "r"))) die ("Невозможно открыть файл $counter_file."); echo "Вы – посетитель № ";

fpassthru($fp); ?>

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

Функция readfile() позволяет распечатывать содержимое файла, даже не вызы+ вая fopen(). В качестве своего единственного аргумента она принимает имя файла, считывает этот файл целиком, а затем распечатывает его в стандартный вывод, воз+ вращая при этом количество считанных байтов (или False в случае ошибки). Рас+ смотрим ее применение в счетчике посещений:

<?php //hit_counter08.php

$counter_file = "./count.dat";

if(!($fp = fopen($counter_file, "r"))) die ("Невозможно открыть файл $counter_file."); $counter = (int) fread($fp, 20);

fclose($fp);

$counter++;

if(!($fp = fopen($counter_file, "w"))) die ("Невозможно открыть файл $counter_file."); fwrite($fp, $counter);

fclose($fp);

echo "Вы – посетитель № "; readfile($counter_file); ?>

Как и в предыдущем сценарии, все операции чтения и записи данных в файл вы+ полняются до вывода на страницу последнего значения счетчика.

Произвольный доступ к данным файла

Вероятно, было бы более эффективно открывать файл для чтения и записи с по+ мощью одного вызова fopen(), однако рассмотренные выше функции лишь позво+ ляют манипулировать данными последовательно, т.е. в том же порядке, в котором эти данные организованы (или будут организованы в файле). Это сильно ограничивает возможности их применения; как только указатель позиции в файле переместился к определенной точке, приходится закрывать файл и открывать его снова, прежде

Файлы и каталоги 307

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

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

fseek()

ftell()

PHP предоставляет множество функций, которые предназначены для того, чтобы прочитать данные из определенной позиции или записать их в определенную пози+ цию файла. Если передать функции fseek() в качестве аргументов дескриптор файла ($fp) и целое смещение (в следующем примере 5), то данная функция переместит указатель позиции в файле, связанном с дескриптором $fp, в позицию, которая опре+ деляется смещением. По умолчанию смещение отсчитывается в байтах от начала файла. Рассмотрим пример:

fseek($fp, 5); $one_char = fgetc($fp);

Данный код помещает указатель позиции в файле, связанном с дескриптором $fp, сразу после пятого байта в этом файле. Следовательно, вызов fgetc() возвращает содержимое шестого байта. Для расчета относительного смещения с помощью одного из следующих значений можно задать третий необязательный аргумент (который на+ зывается в документации whence (точка отсчета)):

SEEK_SET: начало файла плюс смещение;

SEEK_CUR (используется по умолчанию): текущая позиция плюс смещение;

SEEK_END: конец файла плюс смещение.

Функция fseek() необычна, потому что является целочисленной PHP+функцией, которая возвращает 0, а не 1 в случае успеха (а в случае неудачи возвращает -1). Ее нельзя использовать с файлами на удаленных узлах через HTTP или FTP URL.

Функция ftell() принимает дескриптор файла и возвращает текущее смещение (в байтах) указателя позиции в данном файле. Например:

$fpi_offset = ftell($fp); rewind()

Функция работает подобно обратной перемотке ленты в магнитофоне ++++++ она при+ нимает дескриптор файла и переустанавливает указатель позиции в начало этого файла. Вызов

rewind($fp);

функционально аналогичен вызову

fseek($fp, 0);

Как было показано выше, функция fpassthru() выводит данные файла, начиная с текущей позиции. Если данные из файла уже считывались, но желательно вывести все содержимое файла, то сначала необходимо вызвать функцию rewind().

С помощью функции rewind() можно модернизировать сценарий счетчика так, чтобы он открывал файл только однажды и для чтения и для записи:

308 Глава 7

<?php //hit_counter09.php

$counter_file = "./count.dat";

if(!($fp = fopen($counter_file, "r+"))) die ("Невозможно открыть файл $counter_file.");

$counter = (int) fread($fp, 20); $counter++;

echo "Вы – посетитель № $counter."; rewind($fp);

fwrite($fp, $counter); fclose($fp);

?>

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

Практика Навигация по файлу

В следующем примере используются описанные функции fseek(), ftell() и rewind():

<?php //nav_file.php

$name_field_len = 15; $country_code_field_len = 2; $country_field_len = 20; $email_field_len = 30;

if(!($fp = fopen("./address.dat", "r"))){

die ("Невозможно открыть файл с адресными данными.");

}

do{

$address = '';

$field = fread($fp, $name_field_len); $address .= $field;

$field = fread($fp, $country_code_field_len); $address .= $field;

$field = fread($fp, $country_field_len); $address .= $field;

$field = fread($fp, $email_field_len); $address .= $field;

echo "$address<BR>"; }while($field);

rewind($fp);

echo "<BR>";

fseek($fp, $name_field_len);

do{

$country_code = fread($fp, $country_code_field_len); fseek($fp, ftell($fp) + $country_field_len +

$email_field_len +

$name_field_len + 1);

//Примечание: на Win32-платформах '+1' необходимо заменить на '+2'

Файлы и каталоги 309

echo "$country_code<BR>"; }while($country_code);

fclose($fp); ?>

Предполагается, что в текущем каталоге существует файл адресной книги, который называется address.dat и выглядит следующим образом (необходимо очень внима+ тельно ввести данный текст и даже соблюдать количество пробелов в каждом поле):

Wankyu Choi

KRRepublic of Korea

wankyu@whatyoumaycallit.com

James Hetfield

USUnited States

james@headbangers.com

Nomura Sensei

JPJapan

nomura@nosuchsite.com

Результат тестового запуска сценария:

Wankyu Choi KRRepublic of Korea wankyu@whatyoumaycallit.com

James Hetfield USUnited States james@headbangers.com

Nomura Sensei JPJapan nomura@nosuchsite.com

KR

US

JP

Записи в данном файле отделяются друг от друга символами новой строки. (Как уже говорилось, символом новой строки в Windows+платформах является последова+ тельность CRLF, в Macintosh ++++++ CR, а в Linux ++++++ LF.) Каждое поле имеет фиксирован+ ную длину: 15 символов для имени, 2 символа для кода страны и т.д.

Как это работает

Сценарий начинает свою работу с открытия в текущем каталоге файла address.dat в режиме для чтения. Сначала отображаются все записи в том виде, как они есть в файле. Когда функция fread() достигает конца файла, переменная $field получает значе+ ние False и первый цикл прекращается:

if(!($fp = fopen("./address.dat", "r"))){

die ("Невозможно открыть файл с адресными данными.");

}

do{

$address = '';

$field = fread($fp, $name_field_len); $address .= $field;

$field = fread($fp, $country_code_field_len); $address .= $field;

$field = fread($fp, $country_field_len); $address .= $field;

$field = fread($fp, $email_field_len); $address .= $field;

echo "$address<BR>"; }while($field);

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

rewind($fp);

fseek($fp, $name_field_len);

Начинается цикл do...while. В каждой его итерации сначала с помощью функ+ ции fread() переменной $country_code присваивается код страны. Затем указатель

310 Глава 7

позиции перемещается в начало следующего поля с кодом страны, и, наконец, полу+ ченный код выводится на экран.

do{

$country_code = fread($fp, $country_code_field_len);

Точная позиция следующего поля с кодом страны определяется таким образом:

с помощью вызова ftell($fp) определяется текущая позиция указателя;

к данному числу прибавляется общая длина всех оставшихся полей и завер+ шающий символ новой строки.

Используя полученный результат как второй аргумент для функции fseek(), можно установить указатель позиции в соответствующую точку файла:

fseek($fp, ftell($fp) + $country_field_len + $email_field_len + $name_field_len + 1);

Для Windows+платформ длина комбинации CR LF равна 2, а не 1 (как в Linux или Macintosh). Записанное значение выводится на экран и цикл, который длится, пока переменная $country_code содержит значение отличное от False, закрывается:

echo "$country_code<BR>"; }while($country_code);

Наконец, закрывается файл данных:

fclose($fp);

Получение информации о файлах

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

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

file_exists("/home/chris/count.dat")

Данная функция возвращает True, если файл count.dat существует и находится в каталоге /home/chris/, и False ++++++ в противном случае. Проверка ошибок теперь может иметь следующую форму:

<?php //hit_counter_10.php

$counter_file = "./count.dat"; if(file_exists($counter_file)) {

if(!($fp = fopen($counter_file, "r+")))

die("Невозможно открыть файл $counter_file"); $counter = (int) fread($fp, filesize($counter_file)); $counter++;

rewind($fp);

}

Файлы и каталоги 311

else {

if(!($fp = fopen($counter_file, "w")))

die("Невозможно открыть файл $counter_file"); $counter = 1;

}

echo "Вы – посетитель № $counter.";

fwrite($fp, $counter); fclose($fp);

?>

Это только одна из множества функций, которые возвращают полезную информа+ цию о заданном файле. Подобным образом можно использовать функцию filesize(), определяющую точное количество байтов, которые необходимо считать из файла данных. Как и предыдущая функция, filesize() вместо дескриптора файла прини+ мает непосредственно его имя:

filesize("/home/chris/count.dat")

Врезультате такого вызова возвращается размер заданного файла в байтах или False

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

<?php //hit_counter11.php

$counter_file = "./count.dat"; if(file_exists($counter_file)) {

if(!($fp = fopen($counter_file, "r+")))

die("Невозможно открыть файл $counter_file"); $counter = (int) fread($fp, filesize($counter_file)); $counter++;

rewind($fp);

}

...

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

Кроме содержимого, существуют также другие свойства файлов, которые могут со+ общать полезную информацию об этих файлах. Эти свойства главным образом зависят от операционной системы, на которой создаются и модифицируются файлы. На Unix+ платформах, например, таких как Linux, данные свойства включают в себя дату созда+ ния, дату изменения, дату последнего доступа и права пользователей. Добавив немного кода, можно заставить сценарий счетчика отображать время последнего доступа к стра+ нице. Функция fileatime() возвращает время последнего доступа к файлу в формате временных меток Unix, а также дату изменения файла в Windows+формате.

Временная метка Unix представляет собой длинное целое число, которое можно интерпретировать как количество секунд между началом так называемой Unix*эпохи (1 января 1970 года) и указанной датой и временем.

PHP предоставляет две другие связанные с временными свойствами файлов функции.

filectime() возвращает время последнего изменения файла в формате вре+ менной метки Unix. Изменение файла фиксируется при создании файла, запи+ си данных в файл или изменении прав доступа к файлу.

312 Глава 7

filemtime() возвращает время последней модификации файла в формате временной метки Unix. Файл считается модифицированным, если он был соз+ дан или его содержимое изменилось.

При работе с временными метками очень полезной является функция getdate(). Она возвращает ассоциативный массив, содержащий информацию о представленной в виде временной метки дате. Возвращаемый массив содержит такие значения, как год, месяц, день месяца и др. Можно записать возвращаемое функцией getdate() значение в переменную (например, $my_date), а затем получить месяц, обратившись к элементу $my_date[‘month’].

Практика Отображение времени последнего доступа к файлу данных счетчика

В данном примере для получения значений даты для файла, имя которого записа+ но в переменную $counter_file, используются функции fileatime() и getdate().

<?php //last_counter_access.php $counter_file = "./count.dat"; if(file_exists($counter_file)){

$date_str = getdate(fileatime($counter_file)); $year = $date_str["year"];

$mon = $date_str["mon"]; $mday = $date_str["mday"]; $hours = $date_str["hours"];

$minutes = $date_str["minutes"]; $seconds = $date_str["seconds"];

$date_str = "$hours:$minutes:$seconds $mday/$mon/$year";

if(!($fp = fopen($counter_file, "r+"))){ die("Невозможно открыть файл $counter_file");

}

$counter = (int) fread($fp, filesize($counter_file)); $counter++;

echo "Вы – посетитель № $counter.";

echo "Последнее посещение страницы состоялось $date_str"; rewind($fp);

}else{

if(!($fp = fopen($counter_file, "w"))){ die("Невозможно открыть файл $counter_file");

}

$counter = 1;

echo "Вы – посетитель № $counter.";

}

fwrite($fp, $counter); fclose($fp);

?>

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

Соседние файлы в папке web - tec