PHP
Obsługa plików
Szukaj…
Składnia
- int plik odczytu (ciąg $ nazwa_pliku [, bool $ use_include_path = false [, zasób $ kontekst]])
Parametry
Parametr | Opis |
---|---|
Nazwa pliku | Odczytywana nazwa pliku. |
use_include_path | Możesz użyć opcjonalnego drugiego parametru i ustawić go na PRAWDA, jeśli chcesz również wyszukać plik w ścieżce dołączania. |
kontekst | Zasób strumienia kontekstu. |
Uwagi
Składnia nazwy pliku
Większość nazw plików przekazywanych do funkcji w tym temacie to:
- Struny w przyrodzie.
- Nazwy plików można przekazywać bezpośrednio. Jeśli wartości innych typów są przekazywane, są one rzutowane na ciąg. Jest to szczególnie przydatne w
SplFileInfo
, która jest wartością w iteracjiDirectoryIterator
.
- Nazwy plików można przekazywać bezpośrednio. Jeśli wartości innych typów są przekazywane, są one rzutowane na ciąg. Jest to szczególnie przydatne w
- Względny lub absolutny.
- Mogą być absolutne. W systemach uniksowych ścieżki bezwzględne rozpoczynają się od
/
, np./home/user/file.txt
, podczas gdy w systemie Windows ścieżki bezwzględne zaczynają się od dysku, np.C:/Users/user/file.txt
- Mogą być również względne, co zależy od wartości
getcwd
i może ulec zmianie przezchdir
.
- Mogą być absolutne. W systemach uniksowych ścieżki bezwzględne rozpoczynają się od
- Zaakceptuj protokoły.
- Mogą zaczynać się od
scheme://
aby określić opakowanie protokołu do zarządzania. Na przykładfile_get_contents("http://example.com")
pobiera treść z http://example.com .
- Mogą zaczynać się od
- Kompatybilny z Slash.
- Chociaż
DIRECTORY_SEPARATOR
w systemie Windows jest ukośnikiem odwrotnym, a system domyślnie zwraca ukośniki odwrotne dla ścieżek, programista może nadal używać/
jako separatora katalogów. Dlatego w celu zachowania zgodności programiści mogą używać/
jako separatorów katalogów we wszystkich systemach, ale należy pamiętać, że wartości zwracane przez funkcje (np.realpath
) mogą zawierać ukośniki odwrotne.
- Chociaż
Usuwanie plików i katalogów
Usuwanie plików
Funkcja unlink
usuwa pojedynczy plik i zwraca informację, czy operacja się powiodła.
$filename = '/path/to/file.txt';
if (file_exists($filename)) {
$success = unlink($filename);
if (!$success) {
throw new Exception("Cannot delete $filename");
}
}
Usuwanie katalogów z rekurencyjnym usuwaniem
Z drugiej strony katalogi powinny zostać usunięte za pomocą rmdir
. Jednak ta funkcja usuwa tylko puste katalogi. Aby usunąć katalog z plikami, najpierw usuń pliki z katalogów. Jeśli katalog zawiera podkatalogi, może być wymagana rekursja .
Poniższy przykład skanuje pliki w katalogu, rekursywnie usuwa pliki członkowskie / katalogi i zwraca liczbę usuniętych plików (nie katalogów).
function recurse_delete_dir(string $dir) : int {
$count = 0;
// ensure that $dir ends with a slash so that we can concatenate it with the filenames directly
$dir = rtrim($dir, "/\\") . "/";
// use dir() to list files
$list = dir($dir);
// store the next file name to $file. if $file is false, that's all -- end the loop.
while(($file = $list->read()) !== false) {
if($file === "." || $file === "..") continue;
if(is_file($dir . $file)) {
unlink($dir . $file);
$count++;
} elseif(is_dir($dir . $file)) {
$count += recurse_delete_dir($dir . $file);
}
}
// finally, safe to delete directory!
rmdir($dir);
return $count;
}
Funkcje wygody
Raw bezpośrednie IO
file_get_contents
i file_put_contents
zapewniają możliwość odczytu / zapisu z / do pliku do / z łańcucha PHP w jednym wywołaniu.
file_put_contents
można również używać z FILE_APPEND
maski bitowej FILE_APPEND
aby dołączyć do pliku zamiast go obcinać i zastępować. Można go użyć wraz z LOCK_EX
aby uzyskać wyłączną blokadę pliku podczas pisania. Flagi maski bitowej można łączyć z |
operator bitowy-LUB.
$path = "file.txt";
// reads contents in file.txt to $contents
$contents = file_get_contents($path);
// let's change something... for example, convert the CRLF to LF!
$contents = str_replace("\r\n", "\n", $contents);
// now write it back to file.txt, replacing the original contents
file_put_contents($path, $contents);
FILE_APPEND
jest przydatny do dołączania do plików dziennika, a LOCK_EX
pomaga zapobiegać warunkom wyścigu zapisu plików z wielu procesów. Na przykład, aby zapisać do pliku dziennika o bieżącej sesji:
file_put_contents("logins.log", "{$_SESSION["username"]} logged in", FILE_APPEND | LOCK_EX);
CSV IO
fgetcsv($file, $length, $separator)
fgetcsv
analizuje wiersz z sprawdzania otwartych plików dla pól csv. Zwraca pola CSV w tablicy w przypadku sukcesu lub FALSE
w przypadku niepowodzenia.
Domyślnie będzie odczytywał tylko jedną linię pliku CSV.
$file = fopen("contacts.csv","r");
print_r(fgetcsv($file));
print_r(fgetcsv($file,5," "));
fclose($file);
contacts.csv
Kai Jim, Refsnes, Stavanger, Norway
Hege, Refsnes, Stavanger, Norway
Wynik:
Array
(
[0] => Kai Jim
[1] => Refsnes
[2] => Stavanger
[3] => Norway
)
Array
(
[0] => Hege,
)
Czytanie pliku bezpośrednio na standardowe wyjście
plik readfile
kopiuje plik do bufora wyjściowego. readfile () nie stwarza żadnych problemów z pamięcią, nawet przy wysyłaniu dużych plików.
$file = 'monkey.gif';
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
Lub ze wskaźnika pliku
Alternatywnie, aby wyszukać punkt w pliku i rozpocząć kopiowanie na standardowe wyjście, użyj zamiast tego fpassthru
. W poniższym przykładzie ostatnie 1024 bajty są kopiowane na standardowe wyjście:
$fh = fopen("file.txt", "rb");
fseek($fh, -1024, SEEK_END);
fpassthru($fh);
Odczytywanie pliku do tablicy
file
zwraca wiersze w przekazanym pliku w tablicy. Każdy element tablicy odpowiada linii w pliku z nową linią nadal dołączoną.
print_r(file("test.txt"));
test.txt
Welcome to File handling
This is to test file handling
Wynik:
Array
(
[0] => Welcome to File handling
[1] => This is to test file handling
)
Uzyskiwanie informacji o pliku
Sprawdź, czy ścieżka jest katalogiem czy plikiem
Funkcja is_dir
zwraca, czy argument jest katalogiem, a is_file
zwraca, czy argument jest plikiem. Użyj file_exists
aby sprawdzić, czy tak jest.
$dir = "/this/is/a/directory";
$file = "/this/is/a/file.txt";
echo is_dir($dir) ? "$dir is a directory" : "$dir is not a directory", PHP_EOL,
is_file($dir) ? "$dir is a file" : "$dir is not a file", PHP_EOL,
file_exists($dir) ? "$dir exists" : "$dir doesn't exist", PHP_EOL,
is_dir($file) ? "$file is a directory" : "$file is not a directory", PHP_EOL,
is_file($file) ? "$file is a file" : "$file is not a file", PHP_EOL,
file_exists($file) ? "$file exists" : "$file doesn't exist", PHP_EOL;
To daje:
/this/is/a/directory is a directory
/this/is/a/directory is not a file
/this/is/a/directory exists
/this/is/a/file.txt is not a directory
/this/is/a/file.txt is a file
/this/is/a/file.txt exists
Sprawdzanie typu pliku
Użyj typu filetype
aby sprawdzić typ pliku, którym może być:
-
fifo
-
char
-
dir
-
block
-
link
-
file
-
socket
-
unknown
Przekazywanie nazwy filetype
bezpośrednio do typu filetype
:
echo filetype("~"); // dir
Zauważ, że filetype
zwraca wartość false i wyzwala E_WARNING
jeśli plik nie istnieje.
Sprawdzanie czytelności i możliwości zapisu
Przekazywanie nazwy pliku do funkcji is_writable
i is_readable
sprawdza, czy plik jest odpowiednio zapisywalny lub czytelny.
Funkcje zwracają false
wdzięcznie, jeśli plik nie istnieje.
Sprawdzanie czasu dostępu do pliku / modyfikacji
Użycie filemtime
fileatime
i fileatime
zwraca znacznik czasu ostatniej modyfikacji lub dostępu do pliku. Zwracana wartość to uniksowy znacznik czasu - szczegółowe informacje można znaleźć w części Praca z datami i czasem .
echo "File was last modified on " . date("Y-m-d", filemtime("file.txt"));
echo "File was last accessed on " . date("Y-m-d", fileatime("file.txt"));
Uzyskaj części ścieżki za pomocą fileinfo
$fileToAnalyze = ('/var/www/image.png');
$filePathParts = pathinfo($fileToAnalyze);
echo '<pre>';
print_r($filePathParts);
echo '</pre>';
Ten przykład wyświetli:
Array
(
[dirname] => /var/www
[basename] => image.png
[extension] => png
[filename] => image
)
Które mogą być użyte jako:
$filePathParts['dirname']
$filePathParts['basename']
$filePathParts['extension']
$filePathParts['filename']
Parametr | Detale |
---|---|
$ ścieżka | Pełna ścieżka pliku do przeanalizowania |
Opcja $ | Jedna z czterech dostępnych opcji [PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION lub PATHINFO_FILENAME] |
- Jeśli opcja (drugi parametr) nie zostanie przekazana, zwracana jest tablica asocjacyjna, w przeciwnym razie zwracany jest łańcuch.
- Nie sprawdza, czy plik istnieje.
- Po prostu analizuje ciąg na części. W pliku nie przeprowadza się sprawdzania poprawności (bez sprawdzania typu MIME itp.)
- Rozszerzenie jest po prostu ostatnim rozszerzeniem
$path
Ścieżka do plikuimage.jpg.png
to.png
nawet jeśli technicznie jest to plik.jpg
. Plik bez rozszerzenia nie zwróci elementu rozszerzenia w tablicy.
Minimalizuj zużycie pamięci podczas pracy z dużymi plikami
Jeśli musimy przeanalizować duży plik, np. Plik CSV o wielkości większej niż 10 MB, zawierający miliony wierszy, niektórzy używają funkcji file
lub file_get_contents
i kończą się naciśnięciem ustawienia memory_limit
pomocą
Dozwolony rozmiar pamięci XXXXX bajtów wyczerpany
błąd. Rozważ następujące źródło (top-1m.csv ma dokładnie 1 milion wierszy i ma rozmiar około 22 Mb)
var_dump(memory_get_usage(true));
$arr = file('top-1m.csv');
var_dump(memory_get_usage(true));
To daje:
int(262144)
int(210501632)
ponieważ interpreter musiał pomieścić wszystkie wiersze w tablicy $arr
, więc zużył ~ 200 MB pamięci RAM. Zauważ, że nawet nie zrobiliśmy nic z zawartością tablicy.
Teraz rozważ następujący kod:
var_dump(memory_get_usage(true));
$index = 1;
if (($handle = fopen("top-1m.csv", "r")) !== FALSE) {
while (($row = fgetcsv($handle, 1000, ",")) !== FALSE) {
file_put_contents('top-1m-reversed.csv',$index . ',' . strrev($row[1]) . PHP_EOL, FILE_APPEND);
$index++;
}
fclose($handle);
}
var_dump(memory_get_usage(true));
które wyjścia
int(262144)
int(262144)
więc nie używamy żadnego dodatkowego bajtu pamięci, ale analizujemy cały plik CSV i zapisujemy go w innym pliku odwracającym wartość drugiej kolumny. Jest tak, ponieważ fgetcsv
czyta tylko jeden wiersz, a $row
jest zastępowany w każdej pętli.
Plik IO oparty na strumieniu
Otwieranie strumienia
fopen
otwiera uchwyt strumienia plików, który może być używany z różnymi funkcjami do czytania, pisania, wyszukiwania i innymi funkcjami na nim. Ta wartość jest typu resource
i nie może być przekazana do innych wątków utrwalających jej funkcjonalność.
$f = fopen("errors.log", "a"); // Will try to open errors.log for writing
Drugi parametr to tryb strumienia plików:
Tryb | Opis |
---|---|
r | Otwórz w trybie tylko do odczytu, zaczynając od początku pliku |
r+ | Otwarty do czytania i pisania, zaczynając od początku pliku |
w | otwarte tylko do zapisu, zaczynając od początku pliku. Jeśli plik istnieje, spowoduje jego opróżnienie. Jeśli nie istnieje, spróbuje go utworzyć. |
w+ | otwarty do czytania i pisania, zaczynając od początku pliku. Jeśli plik istnieje, spowoduje jego opróżnienie. Jeśli nie istnieje, spróbuje go utworzyć. |
a | otwórz plik tylko do zapisu, zaczynając od końca pliku. Jeśli plik nie istnieje, spróbuje go utworzyć |
a+ | otwórz plik do odczytu i zapisu, zaczynając od końca pliku. Jeśli plik nie istnieje, spróbuje go utworzyć |
x | utwórz i otwórz plik tylko do zapisu. Jeśli plik istnieje, wywołanie fopen zakończy się niepowodzeniem |
x+ | utwórz i otwórz plik do odczytu i zapisu. Jeśli plik istnieje, wywołanie fopen zakończy się niepowodzeniem |
c | otwórz plik tylko do zapisu. Jeśli plik nie istnieje, spróbuje go utworzyć. Zacznie pisać na początku pliku, ale nie opróżni pliku przed zapisaniem |
c+ | otwórz plik do odczytu i zapisu. Jeśli plik nie istnieje, spróbuje go utworzyć. Zacznie pisać na początku pliku, ale nie opróżni pliku przed zapisaniem |
Dodanie t
poza trybem (np. a+b
, wt
itp.) W systemie Windows spowoduje przetłumaczenie zakończeń linii "\n"
na "\r\n"
podczas pracy z plikiem. Dodaj b
za trybem, jeśli nie jest to zamierzone, zwłaszcza jeśli jest to plik binarny.
Aplikacja PHP powinna zamykać strumienie za pomocą fclose
gdy nie są one już używane, aby zapobiec błędowi Too many open files
. Jest to szczególnie ważne w programach CLI, ponieważ strumienie są zamykane tylko wtedy, gdy środowisko wykonawcze jest zamykane - oznacza to, że na serwerach internetowych może nie być konieczne (ale nadal powinno to być , w celu zapobiegania wyciekom zasobów), zamknięcie strumieni jeśli nie oczekujesz, że proces będzie działał przez długi czas i nie otworzy wielu strumieni.
Czytanie
Użycie fread
spowoduje odczytanie podanej liczby bajtów ze wskaźnika pliku lub do momentu spełnienia EOF.
Czytanie wierszy
Korzystanie z fgets
spowoduje odczytanie pliku do momentu osiągnięcia EOL lub odczytania podanej długości.
Zarówno fread
jak i fgets
przesuwają wskaźnik pliku podczas czytania.
Czytanie wszystkiego, co pozostało
Użycie stream_get_contents
spowoduje, że wszystkie pozostałe bajty w strumieniu staną się ciągiem znaków i zwrócą go.
Dostosowywanie położenia wskaźnika pliku
Początkowo po otwarciu strumienia wskaźnik pliku znajduje się na początku pliku (lub na końcu, jeśli używany jest tryb a
). Użycie funkcji fseek
spowoduje przeniesienie wskaźnika pliku do nowej pozycji względem jednej z trzech wartości:
-
SEEK_SET
: Jest to wartość domyślna; przesunięcie pozycji pliku będzie względne względem początku pliku. -
SEEK_CUR
: Przesunięcie pozycji pliku będzie względem bieżącego położenia. -
SEEK_END
: Przesunięcie pozycji pliku będzie względne względem końca pliku. Przekazywanie ujemnego przesunięcia jest najczęstszym zastosowaniem tej wartości; przesunie pozycję pliku do określonej liczby bajtów przed końcem pliku.
rewind
jest wygodnym skrótem fseek($fh, 0, SEEK_SET)
.
Użycie ftell
pokaże bezwzględną pozycję wskaźnika pliku.
Na przykład poniższy skrypt czyta pomija pierwsze 10 bajtów, odczytuje kolejne 10 bajtów, pomija 10 bajtów, odczytuje kolejne 10 bajtów, a następnie ostatnie 10 bajtów w pliku.txt:
$fh = fopen("file.txt", "rb");
fseek($fh, 10); // start at offset 10
echo fread($fh, 10); // reads 10 bytes
fseek($fh, 10, SEEK_CUR); // skip 10 bytes
echo fread($fh, 10); // read 10 bytes
fseek($fh, -10, SEEK_END); // skip to 10 bytes before EOF
echo fread($fh, 10); // read 10 bytes
fclose($fh);
Pisanie
Za pomocą fwrite
zapisuje podany ciąg do pliku, zaczynając od bieżącego wskaźnika pliku.
fwrite($fh, "Some text here\n");
Przenoszenie i kopiowanie plików i katalogów
Kopiowanie plików
copy
kopiuje plik źródłowy z pierwszego argumentu do miejsca docelowego w drugim argumencie. Rozwiązany cel musi znajdować się w katalogu, który jest już utworzony.
if (copy('test.txt', 'dest.txt')) {
echo 'File has been copied successfully';
} else {
echo 'Failed to copy file to destination given.'
}
Kopiowanie katalogów z rekurencją
Kopiowanie katalogów jest bardzo podobne do usuwania katalogów, z tym wyjątkiem, że w przypadku plików używana jest copy
zamiast unlink
, natomiast w przypadku katalogów mkdir
zamiast rmdir
jest używany na początku zamiast na końcu funkcji.
function recurse_delete_dir(string $src, string $dest) : int {
$count = 0;
// ensure that $src and $dest end with a slash so that we can concatenate it with the filenames directly
$src = rtrim($dest, "/\\") . "/";
$dest = rtrim($dest, "/\\") . "/";
// use dir() to list files
$list = dir($src);
// create $dest if it does not already exist
@mkdir($dest);
// store the next file name to $file. if $file is false, that's all -- end the loop.
while(($file = $list->read()) !== false) {
if($file === "." || $file === "..") continue;
if(is_file($src . $file)) {
copy($src . $file, $dest . $file);
$count++;
} elseif(is_dir($src . $file)) {
$count += recurse_copy_dir($src . $file, $dest . $file);
}
}
return $count;
}
Zmiana nazwy / przenoszenie
Zmiana nazwy / przenoszenie plików i katalogów jest znacznie prostsze. Całe katalogi można przenosić lub zmieniać ich nazwy w jednym wywołaniu za pomocą funkcji rename
.
rename("~/file.txt", "~/file.html");
rename("~/dir", "~/old_dir");
rename("~/dir/file.txt", "~/dir2/file.txt");