Differences

This shows you the differences between two versions of the page.

Link to this comparison view

sde:laboratoare:03_python_ro [2020/03/03 19:01]
ioana_maria.culic
— (current)
Line 1: Line 1:
-====== Laborator 3 - Functii de Intrare / Iesire ====== 
-===== Materiale ajutătoare ===== 
  
-  * [[https://​www.slideshare.net/​alexandruradovici/​sde-tp-3-fonctions-dentre-et-sortie | Slide-uri]] 
-  * [[http://​elf.cs.pub.ro/​so/​res/​laboratoare/​lab02-refcard.pdf | lab02-refcard.pdf]] 
- 
-==== Nice to read ==== 
-  * TLPI - Chapter 4, ''​File I/O: The Universal I/O model''​ 
- 
-===== Fișiere. Sisteme de fișiere ===== 
- 
-**Fișierul** este una dintre abstractizările fundamentale în domeniul sistemelor de operare; cealaltă abstractizare este procesul. Dacă procesul abstractizează execuția unei anumite sarcini pe procesor, fișierul abstractizează informația persistentă a unui sistem de operare. Un fișier este folosit pentru a stoca informațiile necesare funcționării sistemului de operare și interacțiunii cu utilizatorul. 
- 
-Un **sistem de fișiere** este un mod de organizare a fișierelor și prezentare a acestora utilizatorului. Din punctul de vedere al utilizatorului,​ un sistem de fișiere are o structură ierarhică de fișiere și directoare, începând cu un director rădăcină. Localizarea unei intrări (fișier sau director) se realizează cu ajutorul unei căi în care sunt prezentate toate intrările de până atunci. Astfel, pentru calea ''/​usr/​local/​file.txt''​ directorul rădăcină '''/'''​ are un subdirector ''​usr''​ care include subdirectorul ''​local''​ ce conține un fișier ''​file.txt''​. 
- 
-Fiecare fișier are asociat, așadar, un nume cu ajutorul căruia se face identificarea,​ un set de drepturi de acces și zone conținând informația utilă. 
- 
-Sistemele de fișiere suportate de sistemele de operare de tip Unix și Windows sunt ierarhice. Sistemele Linux/Unix sunt case-sensitive (''​Data''​ este diferit de ''​data''​),​ iar sistemele Windows sunt case-insensitive. 
- 
-Ierarhia sistemului de fișiere Unix are un singur director cunoscut sub numele de ''​root''​ și notat '''/''',​ prin care se localizează orice fișier (a nu se confunda cu directorul ''/​root'',​ care este home-ul utilizatorului privilegiat,​ root). Notația Unix pentru căile fișierelor este un șir de nume de directoare despărțite prin '''/''',​ urmat de numele fișierului. Există și căi relative la directorul curent '''​.'''​ sau la directorul părinte '''​..'''​. 
- 
-{{ :​sde:​laboratoare:​fs-tree.png?​nolink&​550 |}} 
- 
-În Unix nu se face nicio deosebire între fișierele aflate pe partițiile discului local, pe CD sau pe o mașină din rețea. Toate aceste fișiere vor face parte din ierarhia unică a directorului ''​root''​. Acest lucru se realizează prin ''​montare'':​ sistemele de fișiere vor fi montate într-unul dintre directoarele sistemului de fișiere rădăcină. 
- 
-În Windows există mai multe ierarhii, câte una pentru fiecare partiție și pentru fiecare loc din rețea. Spre deosebire de Unix, delimitatorul între numele directoarelor dintr-o cale este '''​\''',​ și pentru căile absolute trebuie specificat numele ierarhiei în forma ''​C:​\'',​ ''​E:​\''​ sau ''<​nowiki>​\\FILESERVER\myFile</​nowiki>''​ (pentru rețea). Ca și Unix, Windows folosește '''​.'''​ pentru directorul curent și '''​..'''​ pentru directorul părinte. ​ 
-===== Operații pe fișiere ===== 
- 
-În Unix, un **descriptor de fișier** este un întreg care indexează o tabelă cu pointeri spre structuri care descriu fișierele deschise de un proces. În cazul în care un program rulează într-un shell Unix, procesul părinte (shell-ul) deschide pentru procesul copil (programul respectiv) 3 fișiere standard având descriptori de fișiere cu valori speciale: 
- 
-{{ so:​laboratoare:​standard_filedes.png?​300| Descriptorii standard de fișiere}} 
- 
-    * **standard input ** (0) - citirea de la intrarea standard (tastatură) 
-    * **standard output** (1) - afișarea la ieşirea standard (consolă) 
-    * **standard error ** (2) - afișarea la ieşirea standard de eroare (consolă) 
- 
-Un fișier are asociat cursorul de fișier (file pointer) care indică poziția curentă în cadrul fișierului. Cursorul de fișier este un întreg care reprezintă deplasamentul (offset-ul) față de începutul fișierului. 
- 
-Operațiile specifice pentru lucrul cu fișiere: 
-    * **deschiderea/​crearea unui fișier** - înseamnă asocierea unui descriptor de fișier identificat prin numele său (( ''​fopen''​ (ANSI C), ''​open'',​ ''​creat''​ (POSIX), ''​CreateFile''​ (Win32 API) )). ( [[#crearea deschiderea si inchiderea fisierelor|Linux]] ) 
-    * **închiderea unui fișier** - înseamnă eliberarea structurilor de fișier asociate procesului și a descriptorului acelui fișier - doar dacă nu mai există nici o intrare în tabela file descriptorilor care să puncteze spre acea structură (( ''​fclose''​ (ANSI C), ''​close''​ (POSIX), ''​CloseHandle''​ (Win32 API) )). ( [[#crearea deschiderea si inchiderea fisierelor|Linux]] ) 
-    * **citirea dintr-un fișier** - înseamnă copierea unui bloc de date într-un buffer; după ce se realizează citirea se actualizează cursorul de fișier (( ''​fread''​ (ANSI C), ''​read''​ (POSIX), ''​ReadFile''​ (Win32 API) )). ( [[#scrierea si citirea|Linux]] ) 
-    * **scrierea într-un fișier** - înseamnă copierea unui bloc de date dintr-un buffer în fișier; efectuarea scrierii înseamnă și actualizarea cursorului de fișier (( ''​fwrite''​ (ISO C), ''​write''​ (POSIX), ''​WriteFile''​ (Win32 API) )). ( [[#scrierea si citirea|Linux]],​ [[#citirea si scrierea|Windows]] ) 
-    * **poziționarea într-un fișier** - înseamnă schimbarea valorii cursorului de fișier; citirile sau scrierile ulterioare vor porni din locul indicat de acest cursor de fișier ((''​fseek''​ (ANSI C), ''​lseek''​ (POSIX), ''​SetFilePointer''​ (Win32 API) )). ( [[#​pozitionarea in fisier lseek|Linux]] ) 
-    * **schimbarea atributelor unui fișier** - înseamnă stabilirea unor parametri pentru fișier (( ''​fcntl''​ (POSIX), SetFileAttributes (Win32 API) )). ( [[#operatii speciale|Linux]]) 
- 
-===== Operații pe fișiere în Linux ===== 
- 
-Pentru a putea utiliza funcții de manipulare a fișierelor în Python, vom importa biblioteca [[https://​docs.python.org/​3/​library/​os.html|os]]. 
- 
-<code python> 
-import os 
-</​code>​ 
-==== Crearea, deschiderea,​ închiderea si stergerea fișierelor ==== 
- 
-=== open === 
- 
-Pentru deschiderea/​crearea unui fișier se folosește funcția [[https://​docs.python.org/​3/​library/​os.html#​os.open|open]]. 
- 
-<code python> 
-os.open(path,​ flags) #deschidere 
-os.open(path,​ flags, mode) #​creare ​     ​ 
-</​code>​ 
- 
-Valori posibile pentru //flags// sunt: 
-  * os.O_RDONLY ​       open for reading only 
-  * os.O_WRONLY ​       open for writing only 
-  * os.O_RDWR ​         open for reading and writing 
-  * os.O_NONBLOCK ​     do not block on open or for data to become available 
-  * os.O_APPEND ​       append on each write 
-  * os.O_CREAT ​        ​create file if it does not exist 
-  * os.O_TRUNC ​        ​truncate size to 0 
-  * os.O_EXCL ​         error if   * O_CREAT and the file exists 
-  * os.O_SHLOCK ​       atomically obtain a shared lock 
-  * os.O_EXLOCK ​       atomically obtain an exclusive lock 
-  * os.O_NOFOLLOW ​     do not follow symlinks 
-  * os.O_SYMLINK ​      allow open of symlinks 
-  * os.O_EVTONLY ​      ​descriptor requested for event notifications only 
-  * os.O_CLOEXEC ​      mark as close-on-exec 
- 
-Valorile flags sunt reprezentate prin biti, astfel ca pot fi combinate prin operatorul | (sau pe biti). 
- 
-<code python> 
-# open a file in write only and delete all its contents (truncate to 0) 
-os.open(pathname,​ O_WRONLY | O_TRUNC, mode); 
-</​code>​ 
- 
-Valoarea lui //mode// este reprezentata de drepturile noului fisier creat (pe biti). In general, se foloseste un numar in baza 8. 
-Acesta are trei cifre, fiecare cu trei biti. 
- 
-^  r w x  ^  r - x  ^  r - -  ^ 
-|  1 1 1  |  1 0 1  |  1 0 0  | 
-|    7    |    5    |    4    | 
- 
-  * r - read 
-  * w - write 
-  * x - execute 
- 
-Fiecare cifra se refera la: 
-  * prima cifra - permisiunile utilizatorului care detine fisierul 
-  * a doua cifra - permisiunile grupului care detine fisierul ​ 
-  * a treia cifra - permisiunile utilizatorulor care nu detin fisierul si nici nu fac parte din grupul care detine fisierul 
- 
- 
-=== close === 
- 
-Închiderea de fișiere se realizează cu [[https://​docs.python.org/​3/​library/​os.html#​os.close|close]]:​ 
- 
-<code python> 
-os.close(fd) 
-</​code>​ 
- 
-=== unlink === 
- 
-Ștergerea efectivă a unui fișier de pe disk se realizează cu funcția [[https://​docs.python.org/​3/​library/​os.html#​os.unlink|unlink]]:​ 
- 
-<code python> 
-os.unlink(pathname) 
-</​code>​ 
- 
-<note warning> 
-Toate functiile din biblioteca os arunca o exceptie daca operatia nu reuseste. Vom folosi constructia try-except pentru a trata exceptiile. 
-</​note>​ 
- 
-=== Exemplu === 
- 
-Dacă, spre exemplu, dorim să deschidem fișierul ''​in.txt''​ pentru citire și scriere, cu eventuala creare a acestuia, iar fișierul ''​out.txt''​ pentru scriere, cu trunchiere putem folosi următoarea secvență de cod: 
- 
-<code python> 
-import os 
- 
-try: 
-    fd1 = os.open("​in.txt",​ O_RDWR | O_CREAT, 0644) 
- 
-    # will fail if out.txt does not exist 
-    fd2 = os.open("​out.txt",​ O_WRONLY | O_TRUNC) 
-    ​ 
-    os.close(fd1) 
-    os.close(fd2) 
-        
-except Exception as e: 
-    print ("​Error:​ {}"​.format (e)) 
-</​code>​ 
- 
-**Atenție!** O greșeală frecventă este omiterea drepturilor de creare a fișierului (0644 în exemplul de mai sus) când se apelează open cu flag-ul O_CREAT setat. 
- 
-==== Scrierea și citirea ==== 
- 
-=== read === 
- 
-Funcția [[https://​docs.python.org/​3/​library/​os.html#​os.read|read]] e folosită pentru citirea din fișier a maxim ''​count''​ octeți: 
- 
-<code python> 
-os.read(fd, count) 
-</​code>​ 
- 
-Funcția [[https://​docs.python.org/​3/​library/​os.html#​os.read|read]] întoarce un bytestring care contine octetii cititi, cel mult ''​count''​. Când se ajunge la sfârșitul de fișier se va întoarce un bytestring gol. 
- 
-=== write === 
- 
-Funcția [[https://​docs.python.org/​3/​library/​os.html#​os.write|write]] e folosită pentru scrierea în fișier a datelor din str: 
- 
-<code python> 
-os.write(fd,​ str) 
-</​code>​ 
- 
-Valoarea întoarsă este numărul de octeți ce au fost efectiv scriși. În mod implicit nu se garantează că la revenirea din [[https://​docs.python.org/​3/​library/​os.html#​os.read|write]] scrierea în fișier s-a terminat. Pentru a forța actualizarea se poate folosi [[https://​docs.python.org/​3/​library/​os.html#​os.fsync|fsync]] sau fișierul se poate deschide folosind flagul ''​O_FSYNC'',​ caz în care se garantează că după fiecare write fișierul a fost actualizat. 
- 
-**Observație**: ​ 
-Pentru [[https://​docs.python.org/​3/​library/​os.html#​os.read|read]]/​[[https://​docs.python.org/​3/​library/​os.html#​os.write|write]] există versiunile [[https://​docs.python.org/​3/​library/​os.html#​os.pread|pread]]/​[[https://​docs.python.org/​3/​library/​os.html#​os.pwrite]],​ care permit specificarea unui offset în fișier de la care să se efectueaze operația de citire/​scriere. 
- 
-==== Poziționarea în fișier (lseek) ==== 
- 
-=== lseek === 
- 
-Funcția [[https://​docs.python.org/​3/​library/​os.html#​os.lseek|lseek]] permite mutarea cursorului unui fișier la o poziție absolută sau relativă. 
- 
-<code python> 
- ​os.lseek(fd,​ offset, whence) 
-</​code>​ 
- 
-Parametrul ''​whence''​ reprezintă poziția relativă de la care se face deplasarea: ​ 
-  *''​os.SEEK_SET''​ - față de poziția de început 
-  *''​os.SEEK_CUR''​ - față de poziția curentă 
-  *''​os.SEEK_END''​ - față de poziția de sfârșit 
- 
-**Observație** [[https://​docs.python.org/​3/​library/​os.html#​os.lseek|lseek]] permite și poziționări după sfârșitul fișierului. Scrierile care se fac în astfel de zone nu se pierd, ceea ce se obține fiind un fișier cu //goluri//, o zonă care este //​sărită//​ - nu este alocată pe disc.  
- 
-==== Trunchierea fișierelor ==== 
-Pe lângă trunchierea la 0 care se poate face prin apelul ''​open''​ cu flag-ul ''​O_TRUNC'',​ se poate specifica trunchierea unui fișier la o dimensiune specificată,​ prin apelurile de sistem [[https://​docs.python.org/​3/​library/​os.html#​os.ftruncate|ftruncate]] și [[https://​docs.python.org/​3/​library/​os.html#​os.truncate|truncate]]:​ 
- 
-<code python> 
-os.ftruncate(fd,​ length) ​       ​ 
-os.truncate(path,​ length) 
-</​code>​ 
- 
-În cazul [[https://​docs.python.org/​3/​library/​os.html#​os.ftruncate|ftruncate]],​ parametrul ''​fd''​ este file descriptorul obținut cu un apel open, care a asigurat drept de scriere. În cazul [[https://​docs.python.org/​3/​library/​os.html#​os.truncate|truncate]],​ fișierul reprezentat prin ''​path''​ trebuie să aiba drept de scriere. 
- 
-==== Exemplu utilizare operații I/O === 
-<code python> 
-import os 
- 
-# Print the last 100 bytes from a file 
- 
-try: 
-    # open file  
-    fd = os.open("​file.txt",​ O_RDONLY) 
-  
-    # set file pointer at 100 characters _before_ the end of the file 
-    rc = os.lseek(fd,​ -100, SEEK_END) 
-  
-    # read the last 100 characthers ​ 
-    msg = os.read(fd, 100) 
-    bytes_read = len(msg) 
- 
-    print ("the last " + bytes_read + " bytes"​) 
-    print (buf.decode("​utf-8"​)) 
- 
-    # close file 
-    os.close(fd) 
-except Exception as e: 
-    print ("​Error:​ {}"​.format (e)) 
- 
-</​code>​ 
- 
-==== Redirectări ==== 
- 
-În Linux redirectările se realizează cu ajutorul funcțiilor de duplicare a descriptorilor de fișiere [[https://​docs.python.org/​3/​library/​os.html#​os.dup|dup]] și [[https://​docs.python.org/​3/​library/​os.html#​os.dup2|dup2]] (observați diferența dintre cele 2 în link-urile anterioare):​ 
-<code python> 
-os.dup(fd) 
-os.dup2(fd1,​ fd2, inheritable=True) 
-</​code>​ 
-De exemplu, pentru redirectarea ieșirii în fișierul ''​output.txt'',​ sunt necesare două linii de cod: 
-<code python> 
-try: 
-    fd = os.open("​output.txt",​ O_RDWR|O_CREAT|O_TRUNC,​ 0600) 
-    os.dup2(fd, STDOUT_FILENO) 
-except Exception as e: 
-    print ("​Error:​ {}"​.format (e)) 
-</​code>​ 
- 
-===== Exerciții ===== 
- 
-==== Exercițiul 1 - redirect (1p) ==== 
- 
-Intrați în directorul ''​1-redirect''​ și urmăriți conținutul fișierului ''​redirect.py''​. ​ 
- 
-Rulați programul folosind comanda ''​python3 redirect.py''​. 
- 
-Deschideți alt terminal și rulați comanda: 
-<code bash> watch -d lsof -p $(pidof redirect) </​code>​ 
- 
-[[ http://​linux.die.net/​man/​8/​lsof | lsof]] este un utilitar care afișează informații despre fișierele deschise (ce fișiere sunt deschise în sistem, ce fișiere a deschis un anumit user etc). Căutați în manual (''​man 8 lsof''​) pentru a identifica semnificația coloanei FD și a coloanei TYPE. 
- 
-Folosiți comanda ENTER pentru a continua programul. În paralel urmăriți cum se modifică tabela de file-descriptori. 
- 
-În cod, observați parametrii cu care s-a realizat redirectarea cu ajutorul funcției[[https://​docs.python.org/​3/​library/​os.html#​os.dup2|dup2]] (dup2(fd2, 2)). Observați ce se întamplă dacă parametrii sunt în ordine inversă. 
-   * revedeți secțiunea de [[#​redirectari|redirectări]] 
- 
-==== Exercitiul 2 - read-write (3p) ==== 
- 
-mergeti in directorul ''​2-read-write''​ si deschideti fisierul ''​read-write.py''​. 
- 
-=== 2a. Scrieti-va numele (1p) === 
-Puneti numele vostru in variablila //name//. Afisati variabila pe ecran folosind doar functia //write//. Urmariti liniile cu TODO 1. 
- 
-=== 2b. Cititi-va numele (1p) === 
-Cititi de la tastatura variabila //name// folosind doar functia //read//. 
- 
-Functia //read// citeste buffer-e binare, astfel incat nu adauga caracterul \0 in capat. Adaugati caracterul \0. 
- 
-Afisati noul nume pe ecran folosind functia //write//. 
- 
-Urmariti liniile cu TODO 2. 
- 
-=== 2c. Redirectarea iesirii standard (1p) === 
- 
-Scrieti-va numele intr-un fisier numit //​output.txt//​ folosind functia //write//. 
- 
-<​note>​ 
-Redirectati iesirea standard catre fisier. 
- 
-La crearea unui fisier nou, trebuie sa ii setati modul (al treilea parametru al functiei //open//). Modul cel mai des folosit este 0644. 
-</​note>​ 
- 
-Urmariti linii cu TODO 3. 
- 
- 
-==== Exercițiul 3 - lseek (1p) ==== 
- 
-Intrați în directorul ''​3-lseek''​ și urmăriți codul sursă din ''​lseek.py''​. 
-Ce valoare va întoarce al doilea apel al funcției ''​lseek''?​ Decomentați linia de afișare, compilați și rulați pentru verificare. 
- 
-Sursa închide doar file descriptorul ''​fd1''​. Este nevoie să se închidă și file descriptorul ''​fd2''?​ De ce?  
- 
-==== Exercițiul 4 - mcat (3p) ==== 
- 
-Intrați în directorul ''​4-mcat''​. 
- 
-=== 4a. Similitudine cat (1p) === 
- 
-Completați fișierul astfel încât programul rezultat ''​mcat''​ să aibă funcționalitate similară cu a utilitarului ''​cat''​ (urmăriți comentariile cu ''​TODO 1''​) 
- 
-Programul ''​mcat''​ va primi ca argument în linia de comandă numele unui fișier al cărui conținut îl va afișa la ieșirea standard. 
-Nu aveți voie să citiți tot fișierul în memorie. Puteți citi doar bucăți de dimensiune maximum BUFSIZE. 
- 
-**Verificați codul de eroare** întors de apelurile de sistem. ​ 
-Revedeți secțiunile [[#crearea deschiderea si inchiderea fisierelor|Crearea,​ deschiderea și închiderea fișierelor]] și [[#scrierea si citirea|Scrierea și citirea fișierelor]]. 
- 
-Testați cu o comandă de genul: ​ <code bash>​python3 mcat.py Makefile </​code>​ 
- 
-=== 4b. Similitudine cp (1p) === 
- 
-Extindeți funcționalitatea astfel încât output-ul să fie redirectat într-un fișier primit ca al doilea argument - funcționalitate similară cu a utilitarului ''​cp''​. (urmăriți comentariile cu ''​TODO 2''​) 
- 
-Revedeți secțiunea de [[#​redirectari|redirectări]]. 
- 
-Testați funcționalitatea:​ <code bash>​python3 mcat.py Makefile out ; python3 mcat.py out </​code>​ 
- 
-=== 4c. /dev/nasty (1p) === 
- 
-Inițializați fișierul ''/​dev/​nasty'':<​code bash>​./​set_nasty.sh</​code>​ 
- 
-Încercați funcționalitatea de copiere pe fișierul ''/​dev/​nasty'':​ <code bash>​./​mcat /dev/nasty 
-./mcat /dev/nasty out ; ./mcat out </​code>​ 
- 
-Dacă apar diferențe, fiți atenți la ce întorc funcțiile read și write (eventual afișați aceste valori) și reparați problema. 
- 
-Testați **scrierea** cu: <code bash>​python3 mcat.py Makefile /dev/nasty ; cat /​dev/​nasty</​code>​ 
-<note tip> 
-În cazul în care ultima comandă nu produce rezultatul așteptat, cel mai probabil nu ați tratat corect cazurile în care ''​read''/''​write''​ întorc o valoare mai mică decât al treilea parametru.  ​ 
-</​note>​ 
- 
- 
-==== EXTRA ==== 
-    *Operații cu fișiere în Python 
-    *Studiați exemplele din {{so:​laboratoare:​pyfileoperations.zip|arhivă}},​ citiți documentația și observați diferențele între API-uri 
- 
-===== Soluții ===== 
- 
-[[https://​github.com/​UPB-FILS/​sde/​tree/​master/​tp03/​solutions | Solutii]] 
- 
- 
-===== Resurse utile ===== 
- 
-  - [[http://​www.gnu.org/​software/​libc/​manual/​html_node/​Low_002dLevel-I_002fO.html|Low level I/O]] (info libc "​Low-Level I/O") 
-  - [[http://​www.gnu.org/​software/​libc/​manual/​html_node/​Duplicating-Descriptors.html|Duplicating descriptors]] (info libc "​Duplicating Descriptors"​) 
-  - [[http://​www.advancedlinuxprogramming.com/​alp-folder/​alp-apB-low-level-io.pdf|Low level I/O]] (Advanced Linux Programming) 
sde/laboratoare/03_python_ro.1583254915.txt.gz · Last modified: 2020/03/03 19:01 by ioana_maria.culic
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0