Статьи

Углубленный обзор файловых операций в PHP

В этом уроке Tuts + Premium мы узнаем, как работать с файловыми операциями с использованием PHP. Это одна из самых фундаментальных тем серверного программирования в целом. Файлы используются в веб-приложениях любых размеров. Итак, давайте научимся читать, писать, создавать, перемещать, копировать, удалять файлы и многое другое.



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

1
2
3
4
$file = «test.txt»;
$new_file = «test_copy.txt»;
 
copy($file, $newfile);
1
2
3
4
$file = «test.txt»;
$new_file = «test_copy.txt»;
 
rename($file, $newfile);

Обратите внимание, что функция называется «переименовать», а не «переместить». Внутренне переименование файла аналогично перемещению его в новое место.

Тот же код работает при изменении каталога файла:

1
2
3
4
$file = «test.txt»;
$new_file = «different/folder/test_copy.txt»;
 
rename($file, $newfile);

Нет такой функции как «удалить». Здесь мы вызываем функцию «unlink».

1
2
3
$file = «test.txt»;
 
unlink($file, $newfile);

Теперь пришло время начать работать с некоторыми файловыми указателями. Существует несколько способов чтения содержимого файла в PHP, но сначала мы начнем с самого простого метода. Позже в статье мы узнаем об альтернативных методах.

Здесь мы будем использовать три файловые функции: fopen, fread и fclose.

01
02
03
04
05
06
07
08
09
10
$file = «test.txt»;
 
// open the file
$fp = fopen($file, «r»);
 
// read 5k data from the file
$contents = fread($fp, 5000);
 
// close the file
fclose($fp);

При открытии файла мы включаем второй параметр, который определяет тип доступа. В этом случае «r» означает «только для чтения». Есть другие типы, которые нам нужно использовать для написания и добавления, о которых мы расскажем позже в этой статье.

Вторым важным моментом, на который следует обратить внимание, является второй параметр функции fread. Это определяет длину данных (в байтах) для чтения из файла. Чтобы прочитать все содержимое файла, нам нужно передать общий размер файла. Функция fileize делает свое дело:

01
02
03
04
05
06
07
08
09
10
$file = «test.txt»;
 
// open the file
$fp = fopen($file, «r»);
 
// read 5k data from the file
$contents = fread($fp, filesize($file));
 
// close the file
fclose($fp);


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

Указатель файла, также известный как «дескриптор» файла, является переменной, которая ссылается на файл. Точное значение этой переменной неважно; все, что нам нужно знать, это то, что он указывает на конкретный файл. Указатель обычно получается открытием файла. В PHP мы используем функцию fopen .

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

1
2
3
4
5
$fp = fopen($file, ‘r’);
// …
 
// always close files
fclose($fp);

Указатель файла на самом деле делает больше, чем просто указывает на файл. Он также указывает на конкретную позицию внутри этого файла. В большинстве случаев при открытии файла указатель файла указывает на начало (или позицию 0) в этом файле. Мы рассмотрим эту концепцию более подробно, когда будем говорить о «поиске» позже в этой статье.


Как и при чтении файлов, существует более одного способа записи в файл. Сначала мы увидим основной способ, а позже в статье мы рассмотрим альтернативы.

Следующий код записывает содержимое переменной $ data в файл test.txt.

01
02
03
04
05
06
07
08
09
10
11
$file = «test.txt»;
$data = «0123456789abc»;
 
// open the file for writing
$fp = fopen($file, «w»);
 
// write data to the file
fwrite($fp, $data);
 
// close the file
fclose($fp);

На этот раз мы использовали другой флаг для открытия файла. Флаг «w» открывает файл для записи и перезаписывает все существующие данные в файле. Если что-то было в файле test.txt, оно будет заменено строкой в ​​$ data.

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

01
02
03
04
05
06
07
08
09
10
11
$file = «test.txt»;
$data = «0123456789abc»;
 
// open the file for writing
$fp = fopen($file, «w»);
 
// write data to the file (only 5 bytes)
fwrite($fp, $data, 5);
 
// close the file
fclose($fp);

Если вы запустите код выше, содержимое test.txt будет только «01234».

Приведенные выше примеры также работают для создания новых файлов. Если файл test.txt не существует, он будет создан автоматически, просто путем вызова функции fopen.

Если все, что вы хотите сделать, это создать новый пустой файл, не записывая в него никаких данных, этот код будет работать:

1
2
3
4
5
6
7
$file = «does_not_exist.txt»;
 
// creates and opens the file
$fp = fopen($file, «w»);
 
// close the file
fclose($fp);

Акт перемещения местоположения файловых указателей называется «поиском». Для поиска мы можем использовать функцию fseek , а чтобы получить позицию заданного указателя, мы можем использовать функцию ftell .

Прежде чем использовать поиск, давайте создадим файл с 10 байтами данных.

1
2
3
4
5
$file = «test.txt»;
$data = «0123456789»;
$fp = fopen($file, «w»);
fwrite($fp, $data);
fclose($fp);

Теперь посмотрим, как ведут себя эти функции. Вот несколько разных примеров:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
$file = «test.txt»;
 
// opening for reading, pointer should be at 0
$fp = fopen($file, «r»);
 
echo «open: «;
// outputs 0
echo ftell($fp).»\n»;
 
// ——
 
// seek to byte 4
fseek($fp, 4);
 
echo «seek 4: «;
// outputs 4
echo ftell($fp).»\n»;
 
// ——
 
// you can go out of bounds
fseek($fp, 9000);
 
echo «out of bounds: «;
// outputs 9000
echo ftell($fp).»\n»;
 
// —-
 
// this is how you get to the end
fseek($fp, 0, SEEK_END);
 
echo «end: «;
// outputs 10
echo ftell($fp).»\n»;
 
// —-
 
// move relative to current position
// negative numbers work
fseek($fp, -3, SEEK_CUR);
 
echo «go back by 3: «;
// outputs 7
echo ftell($fp).»\n»;
 
// —-
 
// or seek relative to the end
// again, negative works
fseek($fp, -3, SEEK_END);
 
echo «go back by 3 from the end: «;
// outputs 7
echo ftell($fp).»\n»;
 
// —-
 
// does the same thing as seeking to 0
rewind($fp);
 
echo «rewind: «;
// outputs 0
echo ftell($fp).»\n»;
 
fclose($fp);

Положение указателя файла может перемещаться с помощью операций, отличных от fseek.

Например, чтение перемещает позицию:

01
02
03
04
05
06
07
08
09
10
11
$file = «test.txt»;
 
$fp = fopen($file, «r»);
 
// reading 6 bytes
$data = fread($fp, 6);
 
// outputs 6
echo ftell($fp);
 
fclose($fp);

Письмо также двигает это:

01
02
03
04
05
06
07
08
09
10
11
12
13
$file = «test.txt»;
 
$data = «0123456789»;
 
$fp = fopen($file, «w»);
 
// writing 10 bytes
fwrite($fp, $data);
 
// outputs 10
echo ftell($fp);
 
fclose($fp);

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


Посмотрев на них, вы увидите, что у нас есть больше вариантов, чем мы говорили ранее. Вот несколько важных факторов, которые следует учитывать при выборе правильного варианта:

  • Открытие с помощью ‘r +’ не удастся, если файл недоступен для записи, даже если вы просто хотите читать, а не записывать в него.
  • Как ‘w’, так и ‘w +’ будут полностью удалять содержимое существующего файла, как только вы откроете его. Если вы намереваетесь добавить данные, вам нужно использовать «a» или «a +».
  • Если вы хотите создать новые файлы, а также предотвратить случайную перезапись и существующий файл, используйте «x» или «x +».
  • При работе с бинарными файлами, такими как изображения, добавьте букву «b» после режима. Например: ‘rb’ или ‘r + b’

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

Возвращает информацию в массиве:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$data = stat(«test.txt»);
 
print_r($data);
/* prints:
 
Array
(
    [0] => 2
    [1] => 0
    [2] => 33206
    [3] => 1
    [4] => 0
    [5] => 0
    [6] => 2
    [7] => 10
    [8] => 1264374556
    [9] => 1264374556
    [10] => 1264373231
    [11] => -1
    [12] => -1
    [dev] => 2
    [ino] => 0
    [mode] => 33206
    [nlink] => 1
    [uid] => 0
    [gid] => 0
    [rdev] => 2
    [size] => 10
    [atime] => 1264374556
    [mtime] => 1264374556
    [ctime] => 1264373231
    [blksize] => -1
    [blocks] => -1
)
 
*/

Данные разбиты на 12 частей и повторяются сначала с помощью цифровых клавиш, а затем снова с помощью строковых ключей.

В руководстве по PHP есть таблица, объясняющая каждый из них:



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

Эта функция возвращает все содержимое данного файла. Также вам не нужно иметь дело с файловыми указателями вообще.

1
2
3
$file = «test.txt»;
 
$contents = file_get_contents($file);

Функция принимает еще четыре дополнительных параметра, как описано в руководстве по PHP . Например, чтобы прочитать только часть файла, вы можете установить четвертый и пятый параметры:

1
2
3
4
$file = «test.txt»;
 
// reads 5 bytes of data, starting at byte 3
$contents = file_get_contents($file, NULL, NULL, 3, 5);

Да, эта функция называется просто file , и она снова возвращает все содержимое файла. Но на этот раз содержимое разделено на массив символом новой строки.

Давайте создадим файл со следующим содержимым:

1
2
3
4
5
abc
123
empty line after this
 
last line

Теперь используйте функцию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$file = «test.txt»;
 
$contents = file($file);
 
print_r($contents);
 
/* prints
 
Array
(
    [0] => abc
 
    [1] => 123
 
    [2] => empty line after this
 
    [3] =>
 
    [4] => last line
)
 
 
*/

Это может быть неочевидно, но в каждом элементе массива есть символ новой строки. Если они вам не нужны, вы можете обрезать их вручную или использовать флаг FILE_IGNORE_NEW_LINES:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
$file = «test.txt»;
 
$contents = file($file, FILE_IGNORE_NEW_LINES);
 
print_r($contents);
 
/* prints
 
Array
(
    [0] => abc
    [1] => 123
    [2] => empty line after this
    [3] =>
    [4] => last line
)
 
*/

Если вам не нужны пустые строки, вы можете использовать флаг FILE_SKIP_EMPTY_LINES:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
$file = «test.txt»;
 
$contents = file($file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
 
print_r($contents);
 
/* prints
 
Array
(
    [0] => abc
    [1] => 123
    [2] => empty line after this
    [3] => last line
)
 
*/

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

1
2
3
4
$file = «test.txt»;
$data = «0123456789»;
 
file_put_contents($file, $data);

Это альтернативы функции stat для получения информации о файле.

01
02
03
04
05
06
07
08
09
10
$file = «test.txt»;
 
// gets the file size
echo filesize($file);
 
// gets the last time the file was modified
echo filemtime($file);
 
// gets the last time the file accessed
echo fileatime($file);

Unix-подобные системы имеют довольно подробный стандарт разрешений для файлов. В системах Windows это немного проще. Весь предмет прав доступа к файлам может быть длинным, и на нем может быть написана отдельная статья. Поэтому вместо этого мы рассмотрим только две простые концепции прав доступа к файлам: быть «читаемым» и «доступным для записи».

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

1
2
3
4
5
6
7
8
9
$file = «test.txt»;
 
if (is_readable($file)) {
    // file is readable
}
 
if (is_writable($file)) {
    // file is writable
}

Чтобы установить права доступа к файлу, мы используем функцию chmod :

1
2
3
4
5
6
7
$file = «test.txt»;
 
// makes the file read-only
chmod($file, 0444);
 
// makes the file readable and writable
chmod($file, 0666);

Приведенный выше код должен работать как в системах Unix, так и в системах Windows. Тем не менее, вы не сможете установить разрешения, если у вас нет файла или у вас есть какие-либо права доступа к нему.

Чтобы лучше понять chmod и права доступа к файлам в целом, вы можете проверить эти ссылки:


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

Самый простой способ прочитать и вывести файл:

1
2
3
$file = «test.txt»;
 
echo file_get_contents($file);

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

Лучшая идея для этого — использовать цикл для чтения только небольших кусков файла за раз. Для условия цикла мы будем использовать функцию feof :

01
02
03
04
05
06
07
08
09
10
11
$file = «test.txt»;
 
$fp = fopen($file, ‘r’);
 
while (!feof($fp)) {
 
    echo fread($fp, 4092);
 
}
 
fclose($fp);

Приведенный выше код загружает в память только 4 КБ данных одновременно, и это значительно уменьшает использование памяти для вывода больших файлов. Функция feof возвращает true, когда мы достигаем конца файла, и это выходит из цикла.