Menu

Fisiere in PHP: fopen,fgets,moduri si altele

La inceput am dorit sa pun toata treaba cu fisiere intr-un singur articol dar mi-am dat seama ca mai bine il sparg in mai multe parti, nu vreau sa am articole in care sa va plictisiti de la prea mult citit. Dupa cum am promis in Lucrul cu fisiere in PHP voi oferii alternative recomandate dar si mai complicate pentru functiile file_get_contents si file_put_contents deja dezbatatute de noi in articolul Inlcude si file_get/put_contents. Cu toate acestea in acest articol voi face abuz de exemple pentru a ma asigura ca ati inteles deoarece este o lectie mai complicata ca cele anterioare, asemanatoare lectiei Regex in PHP.

1. Scurta Introducere

Am vorbit in lectia Tipuri de Variabile despre tipul resource. Ei in aceasta lectie vom vorbii foarte mult despre acest tip deoarece functiile pentru fisiere din aceasta lectie au un parametru obligatoriu si special pentru acest tip. Sintaxa pentru lucrul cu fisiere arata cam asa(urmariti comentariile):
<?php
# Functia fopen returneaza acel tip de date numit resource
# fh vine de file handle si acest nume pentru valoarea returnata de fopen este folosit foarte des  
$fh = fopen('exemplu.txt','a+'); // deschidem fisierul in modul citire si scriere si pointer la sfarsit

// Aici ar venii sa scriem sau citim fisierul

# Un fisier deschis consuma resurse, este extrem de recomandat sa-l inchideti
fclose($fh);// inchidem fisierul
?>

2. Moduri de deschidere a unui fisier

Inainte sa invatam despre dechiderea unui fisier trebuie sa stim si in ce fel sa-l deschidem. Acest capitol nu are exemplu si este unul extrem de plictisitor dar si in acelasi timp este unul din cele mai importante capitole din aceasta lectie. Pointerul(un cuvant care-l veti intalnii de multe ori in aceasta lectie) este locul(masurat in bytes) de unde se face scrierea sau citirea, ca pointerul de la mouse. Urmariti mai jos urmatorul tabel:
Mod Descriere
r Deschidere doar pentru citire. Pointerul la inceputul fisierului. Modul se mai numeste si mod citire
r+ Deschidere atat pentru citire cat si scriere. Pointer la inceputul fisierului.
w Deschidre doar pentru scriere. Pointerul este la inceputul fisierului iar tot continutul fisierului este sters in momentul deschiderii. In caz ca fisierul nu exista se incearca crearea lui. Modul se mai numeste si mod scriere.
w+ Deschidere atat pentru scriere cat si pentru citire. Pointerul este la inceputul fisierului iar tot continutul fisierului este sters in momentul deschiderii. In caz ca fisierul nu exista se incearca crearea lui.
a Deschidere doar pentru scriere. Pointerul este la sfarsitul fisierului. Daca fisierul nu exista se incearca crearea lui. Modul se mai numeste si mod adaugare.
a+ Deschidere atat pentru scriere cat si pentru citire. Pointerul este la sfarsitul fisierului. Daca fisierul nu exista se incearca crearea lui.
x Fiserul este creat si deschis pentru scriere. Pointerul la inceputul fisierului. In caz ca fisierul exista se genereaza o eroare de tip E_WARNING iar functia fopen returneaza false.
x+ Fiserul este creat si deschis pentru scriere si citire. Pointerul la inceputul fisierului. In caz ca fisierul exista se genereaza o eroare de tip E_WARNING iar functia fopen returneaza false.

3. Citirea unui fisier

In caz ca nu prea ati inteles capitoulul cu Moduri de deschidere a unui fisier mai cititi-l odata, nu este obligatoriu sa-l stii pe dinafara deoarece oricum o sa prinzi din mers dar este recomandat sa stii deja cat mai mult din el.
  • Pentru deschiderea unui fisier se foloseste functia resource fopen ( string $fisier , string $mod [, bool $use_include_path = false [, resource $context ]] ). Explicatia pentru parametrii ei este urmatoarea:
    • $fisier - fisierul ce va fi deschis
    • $mod - Modul despre care am vorbit in capitolul Moduri de deschidere a unui fisier
    • $use_include_path - sa se foloseasca path-ul pentru includerea de fisiere stabilit in php.ini
    • $context - Adaugat din PHP 5.0, cu toate ca nu o sa aveti nevoie puteti citii despre Streams pe site-ul oficial

  • Pentru citirea unei linii dintr-un fisier se foloseste functia string fgets ( resource $handle [, int $length ] ). Explicatia pentru parametrii ei este urmatoarea:
    • $handle - handle-ul fisierului, valoarea returnata de functia fopen
    • .
    • $length - Lungimea maxima a unei linii. Daca se specifica 5 de exemplu o linie de 20 de caractere va fi impartita in 4 linii
    • .
    Functia returneaza linia iar in caz ca nu reuseste s-o citeasca(daca de exemplu a ajuns la sfarsitul fisierului) returneaza false. Pentru a citii un fisier intreg, pe linii, folosim un while( ati invatat despre el in lectia Instructiunile For si While) si testam de fiecare data daca fgets() returneaza false iar aceasta va fi conditia pentru while.


In exemplul urmator vom incerca sa citim un fisier linie cu linie si vom afisa continutul, nu uitati sa urmariti comentariile.
Cod test.txt:
Prima linie
A doua linie
A treia linie
A patra linie
Cod test.php:
<?php
# Modul r reprezinta citirea fisierului
$fh = fopen('test.txt','r'); // deschidem fisierul in modul citire

# Observa ca atribuim valoarea returnata de fgets direct din conditie
# Iar in caz ca valoarea este false conditia va returna false si while se va oprii
while(($line = fgets($fh)) !== false){
	# Folosim nl2br pentru transforma liniile noi in <br/>
	echo nl2br($line);
}

# Un fisier deschis consuma resurse, este extrem de recomandat sa-l inchideti
fclose($fh);// inchidem fisierul
?>
Output:


Cum mutam pointerul? Cu ajutorul functiei urmatoare:
int fseek ( resource $handle , int $offset [, int $unde = SEEK_SET ] )
Explicatia pentru parametrii este urmatoarea:
  • $handle - handle-ul returnat de functia fopen
  • $offset - locatia pointerului. In bytes(aproape in caractere, il mutam la al 20-lea caracter s.a.m.d. dar exceptiile sunt diacriticele care sunt pe 2 bytes).
  • $unde - aici sunt 3 constante:
    • SEEK_SET - se seteaza fix pozitia mentionata la $offset
    • SEEK_CUR - se seteaza pozitia curenta plus pozitia mentionata la $offset
    • SEEK_END - se seteaza pozitia sfarsitului fisierului plus pozitia mentionata la $offset

Analizeaza urmatorul exemplu: Cod test.txt:
Prima linie
A doua linie
A treia linie
A patra linie
Cod test.php:
<?php
# Modul r reprezinta citirea fisierului
$fh = fopen('test.txt','r'); // deschidem fisierul in modul citire

# Mutam pointerul cu la cu 20 bytes mai "in jos"
fseek($fh, 20, SEEK_CUR);

# Observa ca atribuim valoarea returnata de fgets direct din conditie
# Iar in caz ca valoarea este false conditia va returna false si while se va oprii
while(($line = fgets($fh)) !== false){
	# Folosim nl2br pentru transforma liniile noi in <br/>
	echo nl2br($line);
}

# Un fisier deschis consuma resurse, este extrem de recomandat sa-l inchideti
fclose($fh);// inchidem fisierul
?>
Output:

4. Scrierea intr-un fisier

Cand scriem intr-un fisier folosim in general modul a+ pentru adaugare de text sau w+ pentru inlocuirea textului. Adaug si plusul deoarece in general cand scriem si citim din fisier.
Pentru scrierea intr-un fisier folosim functia urmatoare:
int fwrite ( resource $handle , string $string [, int $length ] )
Functia returneza numarul de bytes scrisi sau in caz de esesc valaorea booleana false. Descrierea pentru parametrii este urmatoarea:
  • $handle - handleul returnat de fopen
  • $string - textul ce va fi scris
  • $length - numarul maxim de bytes ce vor fi scrisi
Exemplu de mai jos poate parea putin mai complicat deoarece are si html printre el dar este foarte simplu, nu uitati sa urmariti comentariile.
Cod test.txt:
Prima linie
A doua linie
A treia linie
A patra linie
Cod test.php:
<?php
# Modul a reprezinta adaugarea textului
$fh = fopen('test.txt','a+'); // deschidem fisierul in modul citire si scriere si pointer la sfarsit

echo '<table border="5" cellpadding="5"><tr><th>Text initial</th><th>Text nou</th></tr>';
echo '<tr><td>';
while(($line = fgets($fh)) !== false){
	echo nl2br($line);
}
echo '</td><td>';

# PHP_EOL reprezinta &#92;n pe sistemele linux sau \r&#92;n pe sistemele windows
# Constanta PHP_EOL este folosita foarte des pentru portabilitate in fisiere 
fwrite($fh, PHP_EOL.'Linie noua adaugata'); // scriem o linie noua in fisier

fseek($fh, 0); // mutam pointerul la inceputul fisierului pentru a-l putea citii din nou
# Citim din nou fisierul
while(($line = fgets($fh)) !== false){
	echo nl2br($line);
}
echo '</td></tr></table>';

fclose($fh);// inchidem fisierul
?>
Output:

Observati va rog ca a fost adaugata o linie noua si mai observati ca am fost nevoiti sa readucem pointerul la inceputul fisierului pentru a-l citii din nou deoarece functia fgets() muta pointerul de fiecare data cu o linie mai jos si ajunsese la sfarsit.

Mai jos veti vedea un exemplu in care combinam fseek() cu scrierea in fisier. Vom incerca sa modificam a doua linie. Pentru asta folosim functia ftell() pentru a memora unde se termina fiecare linie. Pe langa asta trebuie sa modificam si modul din a in r deoarece modul a nu ne permite sa mutam pointerul cand scriem
Cod test.txt:
Prima linie
A doua linie
A treia linie
A patra linie
Cod test.php:
<?php
# Modul a reprezinta adaugarea textului
$fh = fopen('test.txt','r+'); // deschidem fisierul in modul citire si scriere si pointer la sfarsit

echo '<table border="5" cellpadding="5"><tr><th>Text initial</th><th>Text nou</th></tr>';
echo '<tr><td>';
$sfarsit_linii = array('0'); 
while(($line = fgets($fh)) !== false){
	$sfarsit_linii[] = ftell($fh);
	echo nl2br($line);
}
echo '</td><td>';

fseek($fh, $sfarsit_linii[1]); // mutam cursorul la sfarsitul liniei 1
fwrite($fh, 'Linie noua adaugata'.PHP_EOL); // scriem o linie noua in fisier

fseek($fh, 0); // mutam pointerul la inceputul fisierului pentru a-l putea citii din nou
# Citim din nou fisierul
while(($line = fgets($fh)) !== false){
	echo nl2br($line);
}
echo '</td></tr></table>';

echo '<pre>'.print_r($sfarsit_linii, true).'</pre>';
fclose($fh);// inchidem fisierul
?>
Output:

Dupa cum observati am afisat si array-ul $sfarsit_liniii ca sa observati si cum arata. Dar sper ca mai observati si ceva foarte important. Bytes-ii au fost inlocuiti si nu doar s-a adaugat o linie, observati ca au fost atat inlocuiti cei de pe linia 2 cat si cei de pe linia 3 in caz ca nu doriti sa inlocuiti bytes-ii si doar sa adaugati o linie eu am gasit o metoda interesanta si usoara pentru asta.
Metoda consta in salvarea continutul care exista dupa acel offset si adaugarea lui dupa ce am adaugat linia care o doream, vei observa metoda in actiune in exemplul de mai jos.

Cod test.txt:
Prima linie
A doua linie
A treia linie
A patra linie
Cod test.php:
<?php
# Modul a reprezinta adaugarea textului
$file = 'test.txt';
$fh = fopen($file,'r+'); // deschidem fisierul in modul citire si scriere si pointer la sfarsit

echo '<table border="5" cellpadding="5"><tr><th>Text initial</th><th>Text nou</th></tr>';
echo '<tr><td>';
$sfarsit_linii = array('0'); 
while(($line = fgets($fh)) !== false){
	$sfarsit_linii[] = ftell($fh);
	echo nl2br($line);
}
echo '</td><td>';

fseek($fh, $sfarsit_linii[1]); // mutam cursorul la sfarsitul liniei 1
# Vom prelua continutul dupa offset intr-o variabila si il vom adauga
$continut_dupa_offset = file_get_contents($file, false, null, $sfarsit_linii[1]);
fwrite($fh, 'Linie noua adaugata'.PHP_EOL.$continut_dupa_offset); // scriem o linie noua in fisier

fseek($fh, 0); // mutam pointerul la inceputul fisierului pentru a-l putea citii din nou
# Citim din nou fisierul
while(($line = fgets($fh)) !== false){
	echo nl2br($line);
}
echo '</td></tr></table>';

fclose($fh);// inchidem fisierul
?>
Output:

5. Discutie pe tema blocarii unui fisier

Am remarcat pe alte site-uri , in special cel al lui MarPlo in care se atragea atentia la accesarea simultana de catre doi vizitatori a unui counter de vizitatori. Ce indica problema aceasta?
Pai daca counter-ul ar fi de exemplu la 300 de vizite , ambii vizitatori ar rescrie counter-ul la 301 de vizite , ceea ce nu e tocmai bine daca dorim rezultate corecte sau avem un site foarte vizitat. Eu insa am preferat o rezolvare putin mai diferita, care o vedeti mai jos:
Creati un folder numit vizite. Cand un vizitator acceseaza site-ul se creeaza un fisier nou acolo(cu numele un numar random , nu numele conteaza). Numarul de vizite va fi numarul de fisiere , ok? Acum si putina optimizare. In alt folder decat vizite, de exemplu fix in folderul public_html, se creeaza un fisier vizite.txt. Voi veti programa un cronjob pe un fisier .php sa stearga periodic(odata pe ora de exemplu) fisierele din folderul vizite si sa adauge numarul lor in fisierul vizite.txt(sa adune la numarul deja existent in vizite.txt) iar numarul de vizite va fi numarul de fisiere adunat cu numarul din vizite.txt si astfel veti rezolva cazuri de genul.


Ti-a placut articolul? Asigura-te ca-ti dam de veste cand publicam altele noi.


Tags: Programare Web, Tutoriale, Php, Curs Php

Comments Nota 0 din 0