Sunteți pe pagina 1din 29

Lucrarea 2.

Programarea în limbajul Python - lucrul cu fișiere și


directoare
În această lucrare veţi învăţa:

 Cum să gestionați fișierele text în Python


 Cum să gestionați fișierele CSV

?  Cum să gestionați fișierele cu diacritice


 Cum să utilizați și gestionați directoarele (folderele) și interfața
cu sistemul de operare

Fișiere text, CSV, fișiere cu diacritice, forldere, interfața, sistem de


Cuvinte cheie: operare

Cuprins
Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare ...................................1
2.1 Gestiunea fișierelor text în Python ...................................................................................2
2.2 Gestiunea fișierelor CSV ....................................................................................................8
2.3 Fișiere cu diacritice......................................................................................................... 16
2.4 Foldere şi interfaţa cu sistemul de operare ................................................................... 25
2.5 Bibliografie ..................................................................................................................... 29
Gestiunea fișierelor text în Python 2

2.1 Gestiunea fișierelor text în Python


În deschiderea sau salvarea de fișiere prin programe Python, se lucrează cu căi relative la
rădăcina Python (C:\Python27). Aceste căi vor fi frecvent folosite ca argumente de tip string în funcțiile
ce accesează sistemul de fișiere. Stringurile Python folosesc o serie de caractere cu interpretări speciale,
precum \ (folosit la inserarea unor caractere netastabile). Pentru a evita ca aceste caractere să fie
interpretate și substituite, stringurile se prefixează cu litera r. Vom folosi acest prefix pentru a evita ca în
căile relative caracterul \ să fie substituit.

Pentru exerciții, creați un fișier C:\Python27\Exemple\fisier1.txt cu conținutul:


Ana are mere.
aaa
xxx
(cu Enter după ultima linie!)

Apoi, la prompter:
>>> fisier=open(r'Exemple\fisier1.txt')
>>> fisier
<open file 'Exemple\fisier1.txt', mode 'r' at 0x02DC6F98>
S-a deschis fișierul într-un buffer (zonă temporară de memorie) stocat în variabila fisier. La
afișarea variabilei se indică adresa bufferului și modul 'r' (read-only, modul implicit).
>>> fisier.name
'Exemple\\fisier1.txt'
- numele fișierului
>>> fisier.read()
'Ana are mere.\naaa\nxxx\n'
- returnează conținutul integral al fișierului sub forma unui string; codul \n reprezintă sfârșitul
de linie; a se observa că după citire fișierul se "termină" – un cursor imaginar parcurge tot conținutul
citit din fișier, de aceea următoarea tentativă de citire eșuează:
>>> fisier.readlines()
[]
Pentru a muta cursorul înapoi pe începutul fișierului:
>>> fisier.seek(0)
>>> fisier.readlines()
['Ana are mere.\n', 'aaa\n', 'xxx\n']
- de data aceasta conținutul e returnat ca o listă de linii
>>> fisier.seek(0)
>>> fisier.readline()
'Ana are mere.\n'
>>> fisier.readline()
'aaa\n'
>>> fisier.readline()
'xxx\n'
- de data aceasta fișierul s-a citit linie cu linie; ca și în cazul readlines, fiecare execuție a deplasat
un cursor imaginar prin conținutul fișierului, până la sfârșit; recitirea unor porțiuni de fișier presupune
din nou salturi la început sau la poziția dorită

Putem afişa conţinutul fişierului şi într-o manieră mai lizibilă, aşa cum ar arăta acesta în Notepad
sau alt editor de texte:
>>> fisier.seek(0)
>>> print fisier.read()
Ana are mere.
aaa
xxx
3 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

- codurile \n nu se mai văd, au fost substituite cu Enteruri şi conţinutul se vede ca şi cum am


deschide fişierul într-un editor de texte

>>> fisier.seek(3)
>>> fisier.readline()
' are mere.\n'
- de data aceasta nu s-a revenit la început, ci la octetul 3; în fișierele text simple, ce folosesc
doar caractere ASCII, un octet e echivalent cu un caracter, de aceea linia începe doar de la caracterul cu
poziția 3, fără cuvântul "Ana"
>>> fisier.seek(-5,2)
>>> fisier.readline()
'xxx\n'
- acum seek a primit 2 argumente: al doilea este codul poziției de la care se numără saltul: dacă
lipsește se numără de la începutul fișierului, dacă e 1 se numără de la poziția curentă, dacă e 2 se
numără de la sfârșitul fișierului; așadar, are loc un salt cu 5 poziții înainte de finalul fișierului
>>> fisier.seek(0)
>>> fisier.readline(5)
'Ana a'
- după repoziționarea la început, readline s-a apelat cu argumentul 5, deci se citesc doar 5
octeți-caractere
>>> fisier.seek(0)
>>> fisier.read(10)
'Ana are me'
>>> fisier.read(10)
're.\naaa\nxx'
>>> fisier.read(10)
'x\n'
- după repoziționarea la început, read citește câte 10 octeți-caractere odată, mai puțin la sfârșit
când fișierul se termină
>>> fisier.seek(0)
>>> caractere=[x for rand in fisier for x in rand]
>>> caractere
['A', 'n', 'a', ' ', 'a', 'r', 'e', ' ', 'm', 'e', 'r', 'e', '.', '\n', 'a', 'a', 'a', '\n', 'x', 'x', 'x', '\n']
- lista caracterelor din fișier (nu e necesar să folosim for rand in fisier.readlines(), deoarece
obiectul-fișier este direct iterabil, adică poate fi parcurs direct, linie cu linie; in schimb la afisare e
necesar să folosim fisier.readlines() deoarece un obiect, chiar și iterabil, nu e identic cu o listă, nu are o
valoare afișabilă)
>>> fisier.seek(0)
>>> caractere=[x for rand in fisier for x in rand if x not in ' \n']
>>> caractere
['A', 'n', 'a', 'a', 'r', 'e', 'm', 'e', 'r', 'e', '.', 'a', 'a', 'a', 'x', 'x', 'x']
- lista caracterelor din fișier care nu sunt nici spații, nici final de linie; dacă se dorește obținerea
mulțimii caracterelor eliminând repetițiile, se poate converti lista în mulțime ca mai jos:
>>> set(caractere)
set(['A', 'a', 'e', 'm', 'n', 'r', 'x', '.'])

În continuare vom extrage toate cuvintele din fișier, prin două metode. Prima mai anevoioasă,
are rolul de a recapitula câteva funcții importante de prelucrare a stringurilor:
>>> fisier.seek(0)
>>> text=''.join(fisier)
( vorba de două apostroafe, nu o ghilimea!)
>>> text=text.replace('\n',' ')
>>> text
'Ana are mere. aaa xxx '
- cu join s-au unificat toate liniile într-un string unic, folosind șirul vid '' ca și delimitator; din nou,
s-a preferat exprimarea join(fisier) în loc de join(fisier.readlines()) deoarece obiectele-fișier sunt iterabile
Gestiunea fișierelor text în Python 4

>>> text=text.replace('\n',' ')


>>> text
'Ana are mere. aaa xxx '
- cu replace s-au înlocuit toate finalurile de linie cu spațiu
>>> cuvinte=text.split(' ')
>>> cuvinte
['Ana', 'are', 'mere.', 'aaa', 'xxx', '']
- textul obținut a fost spart în fragmente-cuvinte la întâlnirea fiecărui spațiu; deoarece ultimul
caracter era chiar spațiu, ultimul fragment e șirul vid; pentru a evita apariția semnelor de punctuație în
unele cuvinte, la apelarea lui replace se puteau înlocui și acestea cu șirul vid, astfel realizându-se
ștergerea lor

A doua metodă e mai eficientă:


>>> fisier.seek(0)
>>> text=fisier.read()
>>> text.split()
['Ana', 'are', 'mere.', 'aaa', 'xxx']
- join nu mai e necesar deoarece read() aplică automat join între liniile fișierului; replace nu mai
e necesar deoarece split() fără argument elimină automat toate spațiile albe din text, inclusiv Enter-uri,
Tab-uri și spații

>>> fisier.write("123")
- eroare, fișierul e read only deoarece deschiderea cu open are loc implicit în modul r, fapt care
se poate verifica după cum urmează
>>> fisier.mode
'r'
>>> fisier.close()
>>> fisier.closed
True
- s-a închis fișierul, apoi s-a verificat dacă fișierul e închis
>>> fisier=open(r'Exemple\fisier1.txt','r+')
- de data aceasta, fișierul e deschis în modul read-write, simbolizat prin r+, deci vor fi posibile
atât scrieri cât și citiri
>>> fisier.write('111')
>>> fisier.flush()
- scrierea are loc la poziția curentă, adică începutul fișierului; flush are rolul de a salva
modificarea în fișier; fără flush, salvarea efectivă a modificărilor va avea loc abia la închiderea fișierului
>>> fisier.readline()
' are mere.\n'
- se observă că write() a mutat cursorul, linia fiind citită după cele 3 caractere scrise
>>> fisier.seek(0)
>>> fisier.readline()
'111 are mere.\n'
- așa arată prima linie după operația de scriere
>>> fisier.seek(0,2)
- se deplasează cursorul la poziția zero începând de la sfârșitul fișierului, indicat prin codul 2
>>> fisier.write('222')
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['111 are mere.\n', 'aaa\n', 'xxx\n', '222']
- se observă că noul string a fost scris la sfârșit
>>> fisier.seek(0)
>>> fisier.seek(len(fisier.readline())+1)
- poziționare la finalul primei linii
5 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

>>> fisier.write('3')
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['111 are mere.\n', '3aa\n', 'xxx\n', '222']
- se observă că numărul 3 a fost scris la începutul liniei 2
>>> fisier.seek(0,2)
- poziționare la finalul fișierului
>>> fisier.writelines(['4444','5555'])
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['111 are mere.\n', '3aa\n', 'xxx\n', '22244445555']
- numele funcției writelines() induce în eroare: NU se asigură scrierea mai multor rânduri, ci doar
concatenarea stringurilor din lista primită de writelines(); pentru a scrie mai multe rânduri trebuie ca
acele stringuri să conțină în mod explicit codul de final de rând, \n
>>> fisier.seek(0,2)
>>> fisier.writelines(['\n6666\n','77777\n'])
-s-au inserat finaluri de linie în fața primului string, adică la finalul curent al fișierului, și câte un
final de linie după fiecare string inserat
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['111 are mere.\n', '3aa\n', 'xxx\n', '22244445555\n', '6666\n', '77777\n']
- se observă noile rânduri adăugate și finalizarea lor cu \n
>>> fisier.close()
>>> fisier=open(r'Exemple\fisier1.txt','a+')
- fisierul s-a redeschis în modul a+ care reprezintă read-append, cu scrieri posibile doar la finalul
fișierului!
>>> fisier.readlines()
['111 are mere.\n', '3aa\n', 'xxx\n', '22244445555\n', '6666\n', '77777\n']
>>> fisier.seek(0)
>>> fisier.write('8888')
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['111 are mere.\n', '3aa\n', 'xxx\n', '22244445555\n', '6666\n', '77777\n', '8888']
- chiar dacă s-a făcut poziționarea cu seek(0) la început, operația write s-a aplicat la sfârșitul
fișierului!
>>> fisier.truncate(30)
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['111 are mere.\n', '3aa\n', 'xxx\n', '22244']
- fișierul s-a trunchiat la primele 30 de caractere
>>> fisier.seek(-2,2)
- poziționare cu 2 caractere înaintea sfârșitului de fișier
>>> fisier.truncate()
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['111 are mere.\n', '3aa\n', 'xxx\n', '222']
- fără argument, trunchierea se face la poziția curentă
>>> fisier=open(r'Exemple\fisier1.txt','w+')
>>> fisier.readlines()
[]
- la deschiderea în modul write-read, conținutul existent al fișierului se șterge! metoda e folosită
pentru a crea noi fișiere:
Gestiunea fișierelor text în Python 6

>>> f2=open(r'Exemple\fisier2.txt','w+')
- se poate verifica pe disc apariția noului fișier; și modurile w,a,a+ permit crearea de noi fișiere,
dacă primul argument din open indică un fișier inexistent; în absența semnului +, modurile w și a NU
permit utilizarea funcțiilor read/readline/readlines

O problemă ceva mai complicată e înlocuirea de text într-un fișier (tot în această categorie intră
și ștergerea de caractere, care de fapt e o înlocuire cu șirul vid). În modul read-write e posibilă scrierea
peste caracterele existente, dar nu și ștergerea, inserarea de text nou sau înlocuirea cu un text de
lungime diferită. Toate acestea necesită operații la nivel de string (replace), dar aceste operații nu sunt
disponibile și la nivel de fișier. În consecință, se apelează la următoarea succesiune de operații:
1. Deschiderea fișierului în mod read-write;
2. Citirea conținutului integral într-o listă de stringuri;
3. Aplicarea modificărilor asupra stringurilor din listă;
4. Trunchierea totală a conținutului fișierului (golirea sa);
5. Rescrierea stringurilor modificate în fișierul gol.
(practic e vorba de recrearea fișierului cu conținutul modificat)

Salvăm în fișier1.txt conținutul:


Ana are mere.
mere xxx.
aaaaa.
xxx yyy.
Tastăm la prompter:
>>> fisier=open(r'Exemple\fisier1.txt','r+')
>>> continut=fisier.readlines()
>>> continut
['Ana are mere.\n', 'mere xxx.\n', 'aaaaa.\n', 'xxx yyy.\n']
- conținutul curent, copiat în stringuri; același rezultat se putea obține și prin continut=[x for x in
fisier], datorită caracterului iterabil al fișierelor
>>> continutnou=[rand for rand in continut if 'xxx' not in rand]
>>> continutnou
['Ana are mere.\n', 'aaaaa.\n']
- filtrarea stringurilor, cu eliminarea celor care conțin 'xxx'
>>> fisier.seek(0)
>>> fisier.truncate()
- golirea fişierului
>>> fisier.writelines(continutnou)
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['Ana are mere.\n', 'aaaaa.\n']
- conținutul filtrat a fost rescris peste fișierul golit
>>> fisier.seek(0)
>>> continut=fisier.readlines()
>>> continutnou=[rand.replace('Ana','ANA') for rand in continut]
- de data aceasta conținutul a fost filtrat prin înlocuirea cuvântului Ana cu ANA; dacă se dorește
ștergerea unui cuvânt sau string, se folosește tot replace, dar cu șirul vid: replace('Ana','')
>>> continutnou
['ANA are mere.\n', 'aaaaa.\n']
>>> fisier.seek(0)
>>> fisier.truncate()
>>> fisier.writelines(continutnou)
>>> fisier.flush()
7 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

>>> fisier.seek(0)
>>> fisier.readlines()
['ANA are mere.\n', 'aaaaa.\n']
- conținutul fișierului după rescrierea sa

O altă metodă de a scrie într-un fișier este cu ajutorul funcției print(). Print se folosește în mod
normal pentru afișare de mesaje. În exercițiile cu linia de comandă print() poate lipsi deoarece:
>>> print '1234'
e echivalent cu
>>> print('1234')
și cu
>>> '1234'
(totuși, print aduce beneficii în ce privește formatarea textului afișat – interpretează caractere
precum Tab și finalul de linie)

>>> from __future__ import print_function


- funcția print se importă, altfel e substituită automat cu comanda print și nu acceptă argumente
>>> fisier=open(r'fisier3.txt','w+')
- w+ este modul write-read, care e echivalent cu read-write cu diferența că șterge conținutul
curent al fișierului iar dacă acesta nu există, îl creează; r+, adică read-write presupune că fișierul există
deja
>>> fisier.readlines()
[]
>>> rand1='primul rand'
>>> rand2='al doilea rand'
>>> rand3='al treilea rand'
>>> print(rand1,rand2,rand3,sep='\n',end='\n',file=fisier)
- argumentul sep stabilește caracterele ce vor separa valorile rand1,rand2,rand3; argumentul
end stabilește caracterele ce vor încheia fișierul; argumentul file indică obiectul-fișier în care se face
scrierea
>>> fisier.flush()
- salvarea datelor pe disc
>>> fisier.seek(0)
>>> fisier.readlines()
['primul rand\n', 'al doilea rand\n', 'al treilea rand\n']
- afișarea conținutului după scriere; se observă că folosind codul de final de linie ca separator, s-
a obținut un fișier cu mai multe rânduri)
Gestiunea fișierelor CSV 8

2.2 Gestiunea fișierelor CSV


Fișierele CSV sunt fișiere text ce conțin date structurate și nu text. Python pune la dispoziție un
modul CSV cu rol de filtru, care convertește conținutul unui fișier CSV în matrice sau listă de dicționare,
făcând procesarea datelor mai facilă decât dacă le extragem prin operații de căutare în stringuri. Filtrul
funcționează și invers, la salvare se vor converti automat liste, tuple sau dicționare în rânduri de fișiere
text.
Fișierele CSV reprezintă un format interoperabil premergător XML, JSON etc. De fapt, nu există
un standard de interoperabilitate bazat pe fișiere CSV. Acestea s-au consacrat doar pe principiul
popularității și simplității datorită numărului minimal de reguli, bazat pe 3 caractere speciale:
 the lineterminator=Saltul la rând nou (obținut prin apăsarea lui Enter, având codul \n);
 the delimiter=Separatorul de valori de pe un rând (virgula în mod implicit, sau punct-virgulă,
dacă e setat calculatorul pe formatul regional Romanian din Control Panel – Regional Settings -
Format);
 the quotechar=Caracterul de încadrare a valorilor (ghilimelele în mod implicit);
o acestea pot lipsi dar devin obligatorii atunci când valorile conțin ele însele caractere
speciale (virgule, finaluri de linie);
o dacă ghilimelele trebuie să apară în INTERIORUL unei valori, și nu să delimiteze o
valoare, acestea trebuie dublate.
Deci fișierele de tip CSV conțin o succesiune de înregistrări (rânduri) separate prin Enter,iar
valorile de pe fiecare rând sunt separate prin virgule (de aici denumirea Comma Separated Values). Dacă
valorile conțin virgule, pentru a nu fi confundate cu separatorii, necesită încadrarea valorilor în
ghilimele.
Datorită lipsei standardizării, delimitatorii folosiți pot să difere (Tab sau punct-virgulă în loc de
virgulă, alte caractere în loc de sfârșit de linie etc.) de aceea acumularea de date în format CSV din mai
multe surse poate fi problematică (poate fi necesar un efort suplimentar de conversie a caracterelor de
delimitare). Pentru exerciții, asigurați-vă că e selectat formatul regional English pentru a avea garanția
că virgula e caracterul de delimitare (sau setați acest caracter în Control Panel – Regional Settings –
Formats – Customize this format – List separator).
Python 2.7 permite modificarea delimitatorilor atât la scriere cât și la citire, cu excepția celui
pentru trecere la rând nou care poate fi modificat doar la scriere.

Pentru exercițiu, se va crea un fișier cu numele fisier.csv salvat în C:\Python27\Exemple și cu


conținutul (editat în Notepad):
"Ana",30
"Maria",40,"rosu"
Formatul CSV nu impune ca rândurile să aibă același număr de valori. O practică frecventă este
ca datele CSV să fie obținute din tabele create cu diverse programe capabile să exporte fișiere CSV (ex:
Excel)
Datorită popularității CSV, Python oferă un modul de funcții orientate pe acest tip de fișiere.
>>> import csv
>>> fisier=open(r'Exemple\fisier.csv')
>>> fisiercsv=csv.reader(fisier)
- obiectul fisiercsv se obține prin conversia unui obiect fișier obișnuit!
>>> [x for x in fisiercsv]
[['Ana', '30'], ['Maria', '40', 'rosu']]
>>> fisier.seek(0)
>>> fisier.readlines()
['"Ana",30\n', '"Maria",40,"rosu"\n']

A se observa diferența între cele două moduri de accesare a fișierului:


9 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

- dacă se lucrează direct pe obiectul-fișier original, conținutul e accesat ca o listă de stringuri –


cu readlines(), sau ca un string unic – cu read();
- dacă se lucrează pe obiectul fisiercsv, conținutul e accesat ca o listă de liste, fiecare listă fiind la
rândul său împărțită în elementele delimitate de apariția virgulei în fișier; în plus obiectul CSV nu suportă
funcția readlines(), accesarea fiind făcută printr-un ciclu FOR care mută datele într-o matrice
>>> date=[x for rand in fisiercsv for x in rand]
>>> date
[]
- o a doua extragere a datelor eșuează datorită poziționării cursorului în fișier; acesta trebuie
repoziționat dacă se dorește recitirea datelor
>>> fisier.seek(0)
>>> date=[x for rand in fisiercsv for x in rand]
- repoziționarea se face în obiectul-fișier original, extragerea datelor se face din obiectul CSV
>>> date
['Ana', '30', 'Maria', '40', 'rosu']
- s-au extras datele din CSV, ca stringuri, chiar și cele care în fișierul original nu aveau ghilimele
>>> fisier.seek(0)
>>> fisier.readline()
'"Ana",30\n'
>>> fisiercsv.next()
['Maria', '40', 'rosu']
- comparaţi modul în care se citesc datele direct din fişier, cu modul în care se citesc prin filtrul
csv: citirea linie cu linie se face cu next() pe obiectul CSV

Modificăm pe disc fisier.csv, cu datele:


"A,n,a",30
"M
a
r
i
a",70
"Andrei a zis ""Hello"", apoi a plecat", 10

La prompter:
>>> fisier=open(r'Exemple\fisier.csv')
>>> fisiercsv=csv.reader(fisier)
>>> fisier.readlines()
['"A,n,a",30\n', '"M\n', 'a\n', 'r\n', 'i\n', 'a",70\n', '"Andrei a zis ""Hello"", apoi a plecat", 10\n']
>>> fisier.seek(0)
>>> [x for x in fisiercsv]
[['A,n,a', '30'], ['M\na\nr\ni\na', '70'], ['Andrei a zis "Hello", apoi a plecat', ' 10']]

Se observă că la accesarea prin fișierul original, se obține o listă de 7 rânduri; la accesarea prin
CSV, se obțin 3 înregistrări, prezența ghilimelelor face ca virgulele din numele Ana și salturile la rând nou
din numele Maria să NU fie considerate delimitatori, ci caractere normale; la fel, ghilimelele duble de la
Hello fac ca în datele finale acele ghilimele să apară în valori, nedublate, și fără a fi considerate
delimitatori.

Modificăm pe disc fisier.csv, cu alte date:


Nume,varsta,sex
"Ana",30,"f"
"Maria",40,"f"
"Alin",20,"m"
Gestiunea fișierelor CSV 10

La prompter:
>>> fisier=open(r'Exemple\fisier.csv')
>>> fisiercsv=csv.DictReader(fisier)
>>> [x for x in fisiercsv]
[{'Nume': 'Ana', 'sex': 'f', 'varsta': '30'},
{'Nume': 'Maria', 'sex': 'f', 'varsta': '40'},
{'Nume': 'Alin', 'sex': 'm', 'varsta': '20'}]

Dacă obiectul CSV e creat cu DictReader în loc de reader, datele din CSV sunt preluate ca o listă
de dicționare (în loc de matrice). Se observă că în fiecare dicționar, cheile au fost preluate de pe PRIMUL
RÂND al fișierului, iar valorile de pe restul rândurilor. Numărul de dicționare obţinute e egal cu numărul
de rânduri minus 1 (primul fiind considerat "cap de tabel"). Metoda este ideală când datele au o
structură tabelară (ex: au fost exportate din Excel). Dacă dorim să nu se considere primul rând cap de
tabel, putem prelua cheile din alte surse:
Ștergem capul de tabel din fisier.csv, rămânem cu datele:
"Ana",30,"f"
"Maria",40,"f"
"Alin",20,"m"

La prompter:
>>> fisier=open(r'Exemple\fisier.csv')
>>> captabel=['Nume','Varsta','Sex']
>>> fisiercsv=csv.DictReader(fisier,fieldnames=captabel)
>>> [x for x in fisiercsv]
[{'Nume': 'Ana', 'Sex': 'f', 'Varsta': '30'},
{'Nume': 'Maria', 'Sex': 'f', 'Varsta': '40'},
{'Nume': 'Alin', 'Sex': 'm', 'Varsta': '20'}]
- ordinea de afișare a elementelor într-un dicționar poate să difere de la o afișare la alta și poate
să difere de ordinea datelor din fișierul original, deoarece dicționarele nu sunt ordonate

Presupunem că înregistrările CSV au număr de valori diferit de capul de tabel. Modificăm fișierul
pentru a avea o înregistrare mai lungă și una mai scurtă:
"Ana",30,"f"
"Maria",40,"f","Cluj",2000
"Alin",20

La prompter:
>>> fisier=open(r'Exemple\fisier.csv')
>>> captabel=['Nume','Varsta','Sex']
>>> fisiercsv=csv.DictReader(fisier,fieldnames=captabel)
>>> [x for x in fisiercsv]
[{'Nume': 'Ana', 'Sex': 'f', 'Varsta': '30'},
{'Nume': 'Maria', 'Sex': 'f', None: ['Cluj', '2000'], 'Varsta': '40'},
{'Nume': 'Alin', 'Sex': None, 'Varsta': '20'}]
- s-a creat cheia None pentru valorile în plus și valoarea None pentru valorile în minus; eventual
și acestora li se pot aloca valori la momentul accesării datelor, ca mai jos:

>>> fisier=open(r'Exemple\fisier.csv')
>>> captabel=['Nume','Varsta','Sex']
>>> cheiesupl='Alte Date'
>>> valoareimpl='Nu se stie'
>>>fisiercsv=csv.DictReader(fisier,fieldnames=captabel,restkey=cheiesupl,restval=valoareimpl)
>>> [x for x in fisiercsv]
[{'Nume': 'Ana', 'Sex': 'f', 'Varsta': '30'},
{'Alte Date': ['Cluj', '2000'], 'Nume': 'Maria', 'Sex': 'f', 'Varsta': '40'},
{'Nume': 'Alin', 'Sex': 'Nu se stie', 'Varsta': '20'}]
11 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

- s-a alocat o cheie suplimentară pt valorile în plus și o valoare implicită pt valorile în minus

Am amintit deja că fișierele CSV nu sunt standardizate. Spre exemplu, Excel 2007 poate salva
astfel de fișiere în mai multe formate, cu delimitatori diferiți:
Construim în Excel Tabelul:

Nume Varsta Sex


Ion 20 M
An,drei 5 M
Al"ina 66 F
(s-au inserat doi delimitatori în valori pentru a vedea cum sunt exportați de Excel)

Apoi efectuăm 2 salvări în folderul Exemple:


- Save As, selectăm tipul CSV Comma Delimited, numele fisierExcelCSV.csv
- Save As, selectăm tipul Text Tab Delimited, numele fisierExcelTAB.txt (nu e obligatoriu ca
fișierele să aibă extensia csv);
Apoi creăm un al treilea fișier, fisierExcelAltiDelimit.txt cu conținutul:

Nume|Varsta|Sex
Ion|20|m
/An,drei/|5|m
Al"ina|66|f

În linia de comandă deschidem cele 3 fișiere și le afișăm conținutul:


>>> fisier1=open(r'Exemple\fisierExcelCSV.csv')
>>> fisier2=open(r'Exemple\fisierExcelTAB.txt')
>>> fisier3=open(r'Exemple\fisierAltiDelimit.txt')
>>> fisier1.readlines()
['Nume,Varsta,Sex\n', 'Ion,20,m\n', '"An,drei",5,m\n', '"Ali""na",66,f\n']
(separatorul de valori=virgula; separatorul de linii=\n, caracterul de încadrare în cazul apariției
delimitatorilor în valori=ghilimelele; Excel a dublat ghilimeaua din numele Alina pentru a comunica
faptul că aceasta face parte din valoare)

>>> fisier2.readlines()
['Nume\tVarsta\tSex\n', 'Ion\t20\tm\n', '"An,drei"\t5\tm\n', '"Ali""na"\t66\tf\n']
(separator valori=Tab, cu codul \t, separator de linii=\n, caracter de încadrare=ghilimelele; din
nou Excel a dublat ghilimeaua din interiorul valorii)

>>> fisier3.readlines()
['Nume|Varsta|Sex\n', 'Ion|20|m\n', '/An,drei/|5|m\n', 'Al"ina|66|f\n']
(separator valori=|, separator de linii=\n, caracter de încadrare a valorilor = /)
(nu mai e necesară dublarea ghilimelei, deoarece și-a pierdut funcția de încadrare a valorilor; în
acest fișier, dublarea ar trebui aplicată doar la /, acesta fiind noul caracter de încadrare; nici măcar
prezența lui / nu ar fi necesară, deoarece virgula din numele lui Andrei nu mai are funcție de separator)

Observații:
Gestiunea fișierelor CSV 12

 în cele 3 exemple s-au folosit diverși delimitatori; singurul rămas fix e finalul de linie, deoarece
Python 2.7 nu acceptă la citire alt final de linie(pe de altă parte permite să fie modificat când
fișierul e creat din Python, deci poate fi considerat un bug);
 desigur, denumirea de fișier CSV devine improprie când nu mai e folosită virgula (de aceea
ultimele 2 sunt salvate cu extensia TXT), dar în limbajul curent orice fișier inspirat de structura
CSV e considerat ca fiind "de tip CSV".

În extragerea datelor trebuie ținut cont de noii delimitatori:


>>> fisier1.seek(0)
>>> csv1=csv.reader(fisier1,dialect='excel')
>>> [x for x in csv1]
[['Nume', 'Varsta', 'Sex'],
['Ion', '20', 'm'],
['An,drei', '5', 'm'],
['Ali"na', '66', 'f']]
- la convertirea obiectului fișier în CSV trebuie precizat setul de delimitatori care trebuie luați în
considerare; fiecare astfel de set este un dialect; dialectul excel este cel clasic, cu virgulă, \n și ghilimele1
>>> fisier2.seek(0)
>>> csv2=csv.reader(fisier2,dialect='excel-tab')
>>> [x for x in csv2]
[['Nume', 'Varsta', 'Sex'],
['Ion', '20', 'm'],
['An,drei', '5', 'm'],
['Ali"na', '66', 'f']]
- dialectul excel-tab folosește Tab în loc de virgulă, fiind aplicabil la al doilea fișier

>>> fisier3.seek(0)
>>> csv3=csv.reader(fisier3,delimiter='|',quotechar='/')
>>> [x for x in csv3]
[['Nume', 'Varsta', 'Sex'], ['Ion', '20', 'm'], ['An,drei', '5', 'm'], ['Al"ina', '66', 'f']]

- pentru a treia variantă nu există un dialect predefinit; Python permite să se creeze unul cu
ajutorul clasei Dialect, dar cel mai facil e să se precizeze direct separatorul de valori (delimiter) și
caracterul de încadrare (quotechar); separatorul de linii, cum am precizat deja, nu poate fi modificat la
citirea de date

Aceleași metode de indicare a dialectului se pot folosi și dacă datele sunt extrase sub formă de
dicționare (vezi exemplele cu DictReader în loc de reader)

>>> csv3.dialect.delimiter
'|'
>>> csv3.dialect.quotechar
'/'
- prin intermediul clasei Dialect se pot obține informații despre componentele dialectului curent;
acestea nu sunt importante la citirea de date, deoarece separatorii sunt eliminați după extragerea
datelor, dar sunt importante la salvarea de date, deoarece vor fi inserați în fișier

Observații: Python oferă clasa Sniffer, prin care se poate analiza conținutul unui fișier pentru a deduce
dialectul. Rezultatele însă sunt de acuratețe scăzută, deducția făcându-se pe baza numărului de apariții
a fiecărui caracter pe o linie (deci un caracter care apare la fel de des ca separatorul de valori, poate fi

1
Dacă în Control Panel e setat specificul regional Romanian, în loc de virgulă e punct-virgulă!
13 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

confundat cu separatorul). Clasa Sniffer este încă în fază experimentală și nu recomandăm ca


programele să se bazeze pe aceasta pentru a stabili dialectul.

Pentru a putea salva date CSV într-un fişier, acesta trebuie să fie deschis într-un mod care
permite scrierea. În exemplul următor se foloseşte modul write-read prin care se şi creează fişierul:
>>> fisier=open(r'Exemple\fisiernou.csv','w+')
>>> obiectCSV=csv.writer(fisier)
- definirea obiectului ce se va ocupa de salvarea datelor în fişier
>>> date=['Ana',20,'x,x,x','Andrei a spus "Hello" si a plecat']
- am creat o listă cu 4 elemente, ultimele două conţinând şi separatori
>>> obiectCSV.writerow(date)
- spre deosebire de fisier.write() care poate adăuga doar stringuri în fişier, funcţia writerow
poate primi ca argument structuri de date pe care le converteşte în text, utilizând regulile dialectului
CSV curent. Implicit este dialectul Excel, ce foloseşte virgula, finalul de linie şi ghilimelele
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['Ana,20,"x,x,x","Andrei a spus ""Hello"" si a plecat"\r\n']
- fişierul poate fi deschis şi în Excel sau Notepad pentru a observa forma în care s-au salvat
datele; se poate observa că writerow s-a ocupat şi de adăugarea finalului de linie, aici \r\n, şi de
dublarea ghilimelelor la nevoie (observație: \r este codul de final de linie pentru Macintosh, majoritatea
programelor salvează ambele coduri - \r\n - la final de linie pentru a se asigura că va funcţiona pe orice
sistem de operare)
>>> listatuple=[('Ion','b',30),('Alin','b',20)]
>>> fisier.seek(0)
>>> obiectCSV.writerows(listatuple)
- de data aceasta se foloseşte writerows(), pentru a scrie mai multe înregistrări; fiecare element
al listei e convertit într-o înregistrare, începând cu poziţia curentă, începutul, deci parte din conţinut se
va suprascrie; ca şi writerow(), funcţia se ocupă de inserarea delimitatorilor conform cu dialectul Excel
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['Ion,b,30\r\n',
'Alin,b,20\r\n',
'a spus ""Hello"" si a plecat"\r\n]
>>> obiectCSV.dialect.delimiter
','
>>> obiectCSV.dialect.quotechar
'"'
>>> obiectCSV.dialect.lineterminator
'\r\n'
- s-au afişat separatorii dialectului
>>> dictionar={'XX':11,'YY':22}
>>> fisier.seek(0,2)
>>> obiectCSV.writerows(dictionar)
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['Ion,b,30\r\n',
'Alin,b,20\r\n',
'a spus ""Hello"" si a plecat"\r\n',
'Y,Y\r\n',
'X,X\r\n']
- s-au scris DOAR cheile din dicţionar; pentru valori, se lucrează cu dictionar.values(); pentru a
scrie întreg dicţionarul se lucrează cu DictWriter
>>> fisier.seek(0)
>>> fisier.truncate()
>>> fisier.flush()
Gestiunea fișierelor CSV 14

- s-a şters conţinutul fişierului


>>> dictionar={'Nume':'Ion','Nota':4,'Sex':'b'}
>>> valimplicita='Nu se stie'
>>> obiectCSV2=csv.DictWriter(fisier,['Nume','Nota','LocNasterii'],restval=valimplicita)

Observații:
 spre deosebire de DictReader, la scriere e OBLIGATORIU să se specifice capul de tabel, sub forma
unei secvenţe de stringuri;
 apoi, toate dicţionarele salvate în fişier TREBUIE să aibă chei identice cu capul de tabel!
 dacă dicţionarele salvate în fişier au chei în minus faţă de capul de tabel, se inserează valoarea
implicită declarată prin restval;
 dacă dicţionarele salvate au chei în plus faţă de capul de tabel, în mod implicit e eroare (dar
eroarea se poate dezactiva);
 dialectul implicit este tot Excel.
>>> obiectCSV2.writerow(dictionar)
- eroare! dicţionarul conţine cheia Sex, pentru care nu s-a definit o coloană în capul de tabel!
dacă se doreşte ignorarea elementelor pentru care nu s-au pregătit coloane în tabel, obiectul CSV se
creează cu un argument în plus:
>>>obiectCSV2=csv.DictWriter(fisier,['Nume','Nota','LocNasterii'],restval=valimplicita,extrasaction='ignore',dialect='excel-tab')
- extrasaction va indica ignorarea cheilor suplimentare, apoi am schimbat şi dialectul în Excel
Tab, pentru a salva Taburi în loc de virgule
>>> obiectCSV2.writeheader()
>>> obiectCSV2.writerow(dictionar)
- writeheader() salvează capul de tabel, writerow() salvează valorile din dicţionar
>>> fisier.flush()
>>> fisier.seek(0)
>>> fisier.readlines()
['Nume\tNota\tLocNasterii\r\n',
'Ion\t4\tNu se stie\r\n']
- \t sunt codurile pentru Taburi; în Notepad fişierul arată astfel:

Nume Nota LocNasterii


Ion 4 Nu se stie

Desigur, ideal e ca toate dicţionarele salvate în fişierul CSV să aibă aceleaşi chei, atât între ele cât
şi faţă de capul de tabel declarat cu DictWriter. În acest caz, DictWriter îşi poate lua capul de tabel direct
din dicţionare:
>>> listadictionare=[]
>>> listadictionare.append({'Nume':'Ion','Nota':4,'Sex':'b'})
>>> listadictionare.append({'Nume':'Maria','Nota':10,'Sex':'f'})
>>> listadictionare.append({'Nume':'Alin','Nota':3,'Sex':'b'})
>>> fisier.seek(0)
>>> fisier.truncate()
>>> fisier.flush()
- am golit fişierul şi am iniţializat o listă de dicţionare cu aceleaşi chei
>>> obiectCSV2=csv.DictWriter(fisier,listadictionare[0].keys(),quotechar='/',delimiter='|',lineterminator='.\n',quoting=csv.QUOTE_ALL)
- capul de tabel s-a preluat de la cheile primului dicţionar; nu s-au mai luat măsuri pentru
nepotrivirea între chei şi capul de tabel; s-a definit un dialect nou, cu / pe post de ghilimele, | pe post de
virgulă, punct la finalul fiecărei linii, şi opţiunea QUOTE_ALL activă – prin aceasta, toate valorile vor fi
încadrate cu caracterul ce a preluat funcţia ghilimelelor
>>> fisier.seek(0)
>>> fisier.truncate()
>>> fisier.flush()
>>> obiectCSV2.writerows(listadictionare)
>>> fisier.flush()
15 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

>>> fisier.seek(0)
>>> fisier.readlines()
['/Ion/|/4/|/b/.\n',
'/Maria/|/10/|/f/.\n',
'/Alin/|/3/|/b/.\n']

În Notepad:
/Ion/|/4/|/b/.
/Maria/|/10/|/f/.
/Alin/|/3/|/b/.

Toate opţiunile de modificare a dialectului se aplică identic şi dacă folosim csv.writer în loc de
csv.DictWriter.
Toate opţiunile privind modul de deschidere a fişierelor rămân valabile la CSV:
 modul r+ permite şi citire şi scriere, DOAR dacă fişierul există deja;
 modul w+ permite şi citire şi scriere, DAR goleşte fişierul în caz că există şi îl creează dacă nu
există;
 modul a+ permite şi citire şi scriere, NU afectează conţinutul existent, scrierea se face la sfârşit
indiferent de poziţionarea cu seek.
 modul r permite doar citire, fişierul trebuie să existe;
 modul w permite doar scriere, creează fişierul şi îl distruge dacă exista deja;
 modul a permite doar scriere fără a distruge fişierul în caz că exista deja, scrierea se face la
sfârşit.
La acestea se adaugă modurile binare: r+b,rb, etc. Acestea se folosesc pentru a lucra cu fişiere
binare (altele decât de tip text) în care se lucrează la nivel de octet. Funcţiile cu care se lucrează sunt
aceleaşi.
Fișiere cu diacritice 16

2.3 Fișiere cu diacritice


În domeniul Web și implicit în Semantic Web se pune un accent puternic pe internaționalizare și
posibilitatea de a scrie cunoștințe în orice limbă. În acest sens, setul original de caractere (ASCII) este
insuficient deoarece alocă coduri doar pt 256 caractere (folosind 8 biți) - 128 de bază + 128 extinse.
Majoritatea aplicațiilor Web moderne lucrează cu setul Unicode, mult mai extins, folosind un
număr mai mare de biți (până la 32). Deoarece Unicode este incompatibil cu ASCII, s-a creat un set de
coduri intermediar, UTF-8, care asigură compatibilitatea între setul Unicode și setul ASCII, în sensul că
pentru caracterele de bază (ASCII) folosește aceleași coduri ca și ASCII (pe 8biți) iar pentru celelalte
caractere alocă în funcție de necesități (dar tot pachete de câte 8 biți, astfel încât un program care NU
recunoaște Unicode va putea lucra cu caractere Unicode mascate în grupuri de 2-4 caractere ASCII – nu
le va putea afişa, dar nici nu va da eroare şi va putea să le CONSERVE şi să le transfere mai departe,
eventual în alt fişier).
Python poate lucra atât cu stringuri ASCII cât și cu stringuri Unicode (ultimele au prefixul u):

>>> a=u'âî'
>>> a
u'\xe2\xee'
>>> print a
âî

Când se lucrează cu Unicode, la linia de comandă…


>>> a
…nu mai e echivalent cu..
>>> print a
Dacă variabila a e un string cu diacritice, prima variantă afișează coduri de caracter pentru
diacritice, a doua afișează diacriticile propriu-zise.

Observație: Exerciţiile următoare sunt testate pe specificul local English US. Asta înseamnă că nu se pot
tasta diacritice în linia de comandă (dar se pot tasta în programe şi module, acestea solicitând la
salvarea unui cod sursă cu diacritice setarea UTF-8 printr-un comentariu inserat automat la începutul
modulului!). De aceea în linia de comandă se vor tasta codurile caracterelor dorite şi nu caracterele
propriu-zise. Trecerea pe specificul local românesc (cu tastatură Romanian Legacy) va permite tastarea
de diacritice însă pe unele versiuni de Windows poate apare o conversie eronată a caracterelor ș și ț la
afișare. Specificul local se selectează în Control Panel – Region and Language – Administrative – Change
System Locale.

Python poate deschide fișiere cu diacritice prin intermediul modulului codecs.py care asigură
conversia între mai multe sisteme de codificare a caracterelor (şi nu doar caractere, ci şi sunet, video
etc). Pe de altă parte, modulul csv dă eroare dacă întâlneşte caractere Unicode, dar poate lucra cu
sistemul de codificare UTF-8 care e compatibil cu ASCII (pentru caracterele de bază are aceleași coduri,
pentru restul are coduri care vor fi interpretate ca grupuri de caractere ASCII). Conversiile se pot realiza
conform exerciţiilor de mai jos:
>>> x=u'\u0103'
>>> y=u'\u021a'
- s-au iniţializat x şi y cu două caractere cu diacritice; datorită setării specificul local englez nu se
pot tasta diacritice în IDLE și am indicat codurile Unicode
>>> x
u'\u0103'
>>> y
u'\u021a'
>>> print x
17 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

ă
>>> print y
Ț
- se observă afişarea diferită cu numele variabilei faţă de folosirea lui print

În continuare efectuăm conversii DIN UNICODE ÎN UTF8 – acestea sunt necesare ÎNAINTE de
salvarea datelor într-un fişier:
>>> xUTF=x.encode('UTF-8')
>>> xUTF
'\xc4\x83'
- aşa arată codul UTF, comparativ cu codul Unicode de mai sus
>>> print xUTF
ă
- UTF8 permite ca aceste caractere să fie folosite în loc de ă, în programele care nu pot lucra cu
Unicode (pe specificul local Romanian, afişarea e corectă, căci Windows se ocupă de conversia în
Unicode dar numai LA AFIŞARE!)
>>> yUTF=y.encode('UTF-8')
>>> y
u'\u021a'
>>> yUTF
'\xc8\x9a'
>>> print yUTF
Èš
(echivalentul UTF-8 pentru Ţ; specificul local Romanian va afişa Čš, datorită problemelor
Windows legate de litera Ţ)

În continuare efectuăm conversii DIN UTF8 ÎN UNICODE: - acestea sunt necesare DUPĂ citirea
datelor dintr-un fişier, pentru a le putea manipula direct în format Unicode:
>>> xUni=xUTF.decode('UTF-8')
>>> xUni
u'\u0103'
>>> print xUni
ă
>>> yUni=yUTF.decode('UTF-8')
>>> yUni
u'\u021a'
>>> print yUni
Ț

Funcţiile decode şi encode pot efectua conversii la nivel de string. Dacă stringurile apar într-o
structură de date, conversia devine mai anevoioasă:
>>> listaUni=[x,y]
>>> listaUni
[u'\u0103', u'\u021a']
>>> listaUTF=[i.encode('UTF-8') for i in listaUni]
>>> listaUTF
['\xc4\x83', '\xc8\x9a']

Modulul codecs oferă unele funcţii care facilitează conversia listelor (fără a le mai parcurge
explicit), dar rezultatul conversiei are un comportament mai…ciudat:
>>> import codecs
>>> dateUTF=codecs.iterencode(listaUni,'UTF-8')
>>> [x for x in dateUTF]
['\xc4\x83', '\xc8\x9a']
>>> [x for x in dateUTF]
[]
>>> dateUTF
Fișiere cu diacritice 18

<generator object iterencode at 0x025E20D0>

Observații: A doua parcurgere a variabilei dateUTF dă rezultat vid, iar conţinutul variabilei dateUTF nu
poate fi afişat în mod direct! Asta deoarece funcţia iterencode() (şi în general funcţiile ale căror nume
încep cu iter) NU returnează liste, ci obiecte de tip iterator. Mai multe detalii despre iteratori se vor
discuta în capitolul dedicat funcțiilor, deocamdată e important doar faptul că un iterator poate fi parcurs
O SINGURĂ DATĂ. Pentru a conserva datele unui iterator, acesta trebuie convertit în listă:
>>> dateUTF=codecs.iterencode(listaUni,'UTF-8')
>>> listaUTF=list(dateUTF)
>>> [x for x in listaUTF]
['\xc4\x83', '\xc8\x9a']
>>> [x for x in listaUTF]
['\xc4\x83', '\xc8\x9a']
- linia boldată asigură conversia iteratorului în listă, ceea ce permite, în continuare, accesarea
normală a datelor

După aceleaşi reguli funcţionează decodarea:


>>> dateUni=codecs.iterdecode(listaUTF,'UTF-8')
>>> dateUni
<generator object iterdecode at 0x025D58C8>
(conţinutul iteratorului nu poate fi afişat)
>>> listaUni=list(dateUni)
>>> listaUni
[u'\u0103', u'\u021a']
- în urma conversiei la listă s-a putut face afişarea

Aşadar iterdecode şi iterencode sunt utile la a converti secvenţe (şiruri de valori) dar NU şi
structuri de date mai complexe:
 pentru matrici, se converteşte pe rând fiecare linie, apoi liniile se grupează cu un FOR;
 pentru dicţionare, se converteşte lista cheilor, apoi lista valorilor, apoi rezultatele sunt
împachetate cu zip() şi reconvertite în dicţionar cu dict():
Presupunând că există o matriceUTF şi un dicţionarUTF:
>>> matriceUni=[list(codecs.iterdecode(rand,'UTF-8')) for rand in matriceUTF]
>>> dictionarUni=dict((zip(codecs.iterdecode(dictionarUTF,'UTF-8'), codecs.iterdecode(dictionarUTF.values(),'UTF-8'))))
(după cum se vede, s-a rezolvat şi problema conversiei iteratorului: list(), respectiv dict(zip())
asigură conversiile necesare în cele două cazuri)

Pentru a manevra fișiere text (inclusiv CSV) fără a pierde diacriticele, trebuie avute în vedere
două categorii de aspecte:

A. Aspecte legate de fișier (condițiile pe care trebuie să le îndeplinească fișierul);


B. Aspecte legate de mediul de programare (modul în care se importă/exportă fișierul).

Aspecte legate de fişiere


1.Fișierul text să fie salvat cu opțiunea UTF-8 activată (Notepad permite selectarea opțiunii la
Save As, la alte editoare de text trebuie căutată opţiunea Encoding).
2. Dacă e vorba de fișiere XML, XHTML, pe lângă modul de salvare trebuie inclusă și declarația
de codare la începutul fișierului:
19 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

XML:
<?xml version="1.0" encoding="UTF-8"?>
HTML:
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
(aceasta nu e o cerinţă foarte importantă, majoritatea browserelor moderne presupun implicit
că documentul primit e UTF-8 sau detectează acest lucru din modul în care a fost salvat fişierul, de la
punctul 1; următoarele cerinţe sunt însă importante, ele afectând cel mai des afişarea incorectă a
diacriticelor în browsere!)
3. Dacă documentul XML/XHTML este generat dinamic de la server, trebuie modificat și antetul
HTTP; dacă nu avem posibilități de configurare a serverului, se face modificarea HTTP la nivel de pagină;
de exemplu, în PHP:

<?php header('Content-type: text/html; charset=utf-8'); ?>

4. Dacă documentul conţine diacritice preluate din baza de date (MySQL), şi baza de date va
trebui convertită în UTF-8; phpmyadmin oferă astfel de opţiuni sau se poate folosi o comandă SQL:
ALTER TABLE tabel CONVERT TO CHARACTER SET UTF8

Aspecte legate de Python:


1. La încărcarea în Python a conținutului fişierului, acesta trebuie convertit din UTF-8 în Unicode;
2. Dacă fișierul e CSV, conversia trebuie amânată până după extragerea datelor din CSV (deoarece CSV
nu poate lucra cu Unicode);
3. În Python se poate lucra cu Unicode, șirurile de acest tip fiind prefixate cu litera u;
4. La salvarea fișierelor cu diacritice, trebuie să aibă loc recodarea din Unicode în UTF-8.

Funcţiile utilizate sunt:


 modulul codecs, prin funcţia codecs.open, asigură convertirea Unicode implicită, atât la citire
(din UTF8) cât şi la scriere (în UTF8), pe toată durata de la momentul deschiderii fişierului până la
închidere; conversia la deschidere NU poate fi folosită la CSV, deoarece în acel caz conversia
trebuie amânată până când datele ajung în matrice/listă;
 decode asigură conversia unui string din UTF8 în Unicode, deci se foloseşte după ce s-au extras
datele din fişier;encode asigură conversia unui string din Unicode în UTF8, deci se foloseşte chiar
înainte de scrierea datelor în fişier;
 având în vedere că datele transferate în sau din fişiere nu sunt de obicei simple stringuri (ci liste
de stringuri, sau matrici, sau liste de dicţionare în cazul CSV) poate deveni anevoioasă conversia
fiecărui element extras; pentru aceasta tot modulul codecs oferă funcţii de conversie a
structurilor de date: codecs.iterdecode şi codecs.iterencode, dar trebuie să se ţină cont de faptul
că acestea returnează iteratori.

Construim fișierul diacritice.txt în folderul Exemple, cu conținutul:

Nume, Vârstă, Sex


Țicleanu Maria, 20, femeiesc
Pașcu Andrei, 30, bărbătesc

La salvare din Notepad, se folosește Save As și după precizarea numelui se selectează opțiunea
Encoding: UTF-8. Dacă nu se modifică sistemul de codare, Notepad avertizează că unele diacritice se vor
pierde.
Fișiere cu diacritice 20

Revenim în linia de comandă:


>>> import codecs
>>> fisdiac1=codecs.open(r'Exemple\diacritice.txt',encoding='UTF-8')
>>> fisdiac2=open(r'Exemple\diacritice.txt')
- fișierul a fost deschis de două ori, în două obiecte: primul cu decodare la deschidere, al doilea
fără decodare)
>>> fisdiac1.read()
u'\ufeffNume, V\xe2rst\u0103, Sex\r\n\u021aicleanu Maria, 20, femeiesc\r\nPa\u0219cu Andrei, 30, b\u0103rb\u0103tesc'
- faptul că avem conținutul fișierului prefixat cu u arată că acesta e codificat cu Unicode
>>> fisdiac2.read()
'\xef\xbb\xbfNume, V\xc3\xa2rst\xc4\x83, Sex\n\xc8\x9aicleanu Maria, 20, femeiesc\nPa\xc8\x99cu Andrei, 30, b\xc4\x83rb\xc4\x83tesc'
- lipsa prefixului u și frecventele coduri \x sunt semne că textul e codificat cu UTF-8, așa cum era
în fișierul original

Diferența majoră între cele două sisteme de codificare se vede la afișări:


>>> fisdiac1.seek(0)
>>> print fisdiac1.read()
Nume, Vârstă, Sex
Țicleanu Maria, 20, femeiesc
Pașcu Andrei, 30, bărbătesc
- textul convertit în Unicode e afișat corect

>>> fisdiac2.seek(0)
>>> print fisdiac2.read()
Nume, Vârstă, Sex
Èšicleanu Maria, 20, femeiesc
Pașcu Andrei, 30, bărbătesc
- textul rămas în UTF-8 e afișat incorect – se poate observa că fiecare caracter cu diacritice e
substituit cu 2 caractere ASCII; reamintim că UTF-8 înlocuiește caracterele Unicode cu grupuri de 2-4
caractere ASCII pentru a conserva codurile chiar și în programe care nu pot afișa Unicode; pe specificul
local Romanian şi aceste date sunt afişate CORECT, datorită conversiei automate asigurate de Windows
LA AFIŞARE; totuși, în programele Python nu trebuie să ne bazăm pe acest lucru!

În acest exemplu conversia UTF->Unicode s-a făcut chiar la deschiderea fișierului. În cazul CSV
acest lucru nu e posibil, deoarece CSV nu poate lucra cu Unicode și trebuie să lucreze tot cu UTF-8. În
astfel de cazuri conversia la Unicode se amână până când fișierul a trecut de filtrul CSV. Extragem datele
CSV tot din obiectele-fișier precedente:
>>> import csv
>>> csvdiac1=csv.reader(fisdiac1)
>>> csvdiac2=csv.reader(fisdiac2)
- ambele obiecte-fișier s-au convertit cu success în obiecte CSV; eroarea va apare la afișare!
>>> fisdiac1.seek(0)
>>> fisdiac2.seek(0)
>>> print [x for x in csvdiac1]
- eroare UnicodeError! primul obiect CSV, care a primit datele direct în Unicode, NU poate fi
accesat
>>> print [x for x in csvdiac2]
[['\xef\xbb\xbfNume', ' V\xc3\xa2rst\xc4\x83', ' Sex'], ['\xc8\x9aicleanu Maria', ' 20', ' femeiesc'], ['Pa\xc8\x99cu Andrei', ' 30',
'b\xc4\x83rb\xc4\x83tesc']]
- în al doilea caz, al fișierului rămas în UTF-8, datele au putut fi accesate prin CSV

Abia în acest moment datele din CSV se convertesc în Unicode:


>>> fisdiac2.seek(0)
21 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

>>> dateCSVUTF=[x for x in csvdiac2]


>>> dateCSVUnicode=[[x.decode('UTF-8') for x in rand] for rand in dateCSVUTF]
(sau:
import codecs (dacă nu s-a importat deja)
dateCSVUnicode=[codecs.iterdecode(rand,'UTF-8') for rand in dateCSVUTF])
>>> dateCSVUnicode
[[u'\ufeffNume', u' V\xe2rst\u0103', u' Sex'],
[u'\u021aicleanu Maria', u' 20', u' femeiesc'],
[u'Pa\u0219cu Andrei', u' 30', u' b\u0103rb\u0103tesc']]
(dacă decodarea s-a făcut cu codecs.iterdecode, se va afişa o listă de obiecte-iterator,
nu o listă de liste!)
>>> for rand in dateCSVUnicode:
print
for element in rand:
print element+',',

Nume, Vârstă, Sex,


Țicleanu Maria, 20, femeiesc,
Pașcu Andrei, 30, bărbătesc,

A se observa modul de afișare:


 al doilea print se termină cu o virgulă, pentru ca următorul print să continue afișarea pe ACEEAȘI
linie (iar virgula dintre apostroafe are rol de separator între valori);
 afișarea pe același rând durează până la finalizarea unei înregistrări, apoi se execută print fără
argument care face trecerea la un rând nou, unde încep să se afișeze valorile următoarei
înregistrări.

La fel se pune problema și dacă datele sunt extrase prin DictReader:


>>> dictdiac=csv.DictReader(fisdiac2)
>>>fisdiac2.seek(0)
>>> dateCSVUTF=[x for x in dictdiac]
>>> dateCSVUTF
[{' Sex': ' femeiesc', ' V\xc3\xa2rst\xc4\x83': ' 20', '\xef\xbb\xbfNume': '\xc8\x9aicleanu Maria'},
{' Sex': ' b\xc4\x83rb\xc4\x83tesc', ' V\xc3\xa2rst\xc4\x83': ' 30', '\xef\xbb\xbfNume': 'Pa\xc8\x99cu Andrei'}]
(reamintim că DictReader produce o listă de dicționare, cu primul rând din CSV considerat cap
de tabel)
>>> listaUTF=[[(x,dictionar[x]) for x in dictionar] for dictionar in dateCSVUTF]
>>> listaUTF
[[(' Sex', ' femeiesc'), (' V\xc3\xa2rst\xc4\x83', ' 20'), ('\xef\xbb\xbfNume', '\xc8\x9aicleanu Maria')],
[(' Sex', ' b\xc4\x83rb\xc4\x83tesc'), (' V\xc3\xa2rst\xc4\x83', ' 30'),
('\xef\xbb\xbfNume', 'Pa\xc8\x99cu Andrei')]]
(lista de dicționare s-a convertit în listă de liste de tuple-perechi, mai ușor de parcurs și convertit
decât o listă de dicționare)
>>> listaUnicode=[[(x[0].decode('UTF-8'),x[1].decode('UTF-8')) for x in rand] for rand in listaUTF]
>>> listaUnicode
[[(u' Sex', u' femeiesc'), (u' V\xe2rst\u0103', u' 20'), (u'\ufeffNume', u'\u021aicleanu Maria')],
[(u' Sex', u' b\u0103rb\u0103tesc'), (u' V\xe2rst\u0103', u' 30'),
(u'\ufeffNume', u'Pa\u0219cu Andrei')]]
(toate valorile s-au decodat din UTF în Unicode și acum pot fi afișate cu diacritice)
>>> for rand in listaUnicode:
print
for element in rand:
print element[0]+':'+element[1]+',',

Sex: femeiesc, Vârstă: 20, Nume:Țicleanu Maria,


Sex: bărbătesc, Vârstă: 30, Nume:Pașcu Andrei,
Fișiere cu diacritice 22

Prezentăm acelaşi mecanism folosind conversia prin codecs:


>>> import codecs
(dacă nu era deja importat)
>>> dictdiac=csv.DictReader(fisdiac2)
>>> fisdiac2.seek(0)
>>> dateCSVUTF=[x for x in dictdiac]
>>> dateCSVUnicode=[dict(zip(codecs.iterdecode(dictionar,'UTF-8'), codecs.iterdecode(dictionar.values(),'UTF-8'))) for dictionar in
dateCSVUTF]
>>> for dictionar in dateCSVUnicode:
print
for cheie in dictionar:
print cheie+':'+dictionar[cheie]+',',

Reamintim privind codecs.iterdecode:


 iterdecode poate converti direct o secvenţă de stringuri dar nu şi o matrice sau dicţionar; de
aceea în ultimul exemplu s-au decodat mai întâi cheile, apoi valorile, apoi cu zip au fost
reasociate în tuple, care cu dict au fost reconvertite în dicţionar; aceasta operaţie a fost aplicată
într-un ciclu for, pentru fiecare dicţionar din dateCSVUTF;
 în ultimul exemplu am modificat şi ciclul de afişare, comparativ cu precedentul, în care datele
Unicode au fost grupate într-o listă de liste de tuple (aici e o listă de dicţionare).
În ce priveşte salvarea de diacritice în fişiere se foloseşte acelaşi mecanism, dar invers:
 dacă fişierul nu e CSV, textul salvat se converteşte în UTF-8 înaintea apelării funcţiilor
write/writelines;
 dacă fişierul e CSV, datele sunt convertite în UTF-8 înainte de apelarea funcţiilor
writerow/writerows.

>>> import codecs


>>> fisd=codecs.open(r'Exemple\diacritice.txt','r+',encoding='UTF-8')
- fiind deschis prin intermediul modulului codecs, fişierul va permite salvare de diacritice direct,
fără a converti stringurile

>>> fisd.read()
u'\ufeffNume, V\xe2rst\u0103, Sex\r\n\u021aicleanu Maria, 20, femeiesc\r\nPa\u0219cu Andrei, 30, b\u0103rb\u0103tesc'
>>> textdiac=u'\u0219al\u0103u'
- datorită imposibilității de a tasta diacritice în IDLE, stocăm textul cu diacritice cu ajutorul codurilor
Unicode: \u0219=ş,\u0103=ă, coduri ce pot fi citite din datele de mai sus; reamintim că acest bug nu
afectează programele şi modulele Python ci numai linia de comandă; reamintim că stringurile Unicode,
indiferent că au diacritice sau coduri de diacritice, se prefixează cu litera u; reamintim că specificul
Romanian permite tastarea directă în IDLE: textdiac=u'şalău' dar pot apare probleme cu litera ş!)
>>> print textdiac
șalău
(specificul Romanian va afişa ºalãu datorită problemelor Windows legate de codiţele de la ş şi)
>>> fisd.seek(0,2)
- ne deplasăm la finalul fişierului pentru a nu scrie peste conţinutul existent
>>> fisd.write(textdiac)
>>> fisd.flush()
>>> fisd.seek(0)
>>> fisd.read()
u'\ufeffNume, V\xe2rst\u0103, Sex\r\n\u021aicleanu Maria, 20, femeiesc\r\nPa\u0219cu Andrei, 30, b\u0103rb\u0103tesc\u0219al\u0103u'

Se observă textul ataşat la capăt. Sau, mai clar, în Notepad:

Nume, Vârstă, Sex


Țicleanu Maria, 20, femeiesc
Pașcu Andrei, 30, bărbătescșalău
(la crearea fişierului nu s-a dat Enter la finalul ultimei linii).
23 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

Acest exemplu reflectă simplitatea cu care se salvează diacriticele dacă fişierul e deschis cu
codecs.open în loc de open. Codecs, prin opţiunea encoding UTF-8, asigură conversia AUTOMATĂ a
oricăror caractere citite din fişier (de la UTF la Unicode) ŞI a celor scrise în fişier (de la Unicode la UTF).
Dacă nu se foloseşte deschiderea prin codecs, aceste conversii trebuie făcute EXPLICIT:

>>> fisd=open(r'Exemple\diacritice.txt','r+')
>>> fisd.read()
'\xef\xbb\xbfNume, V\xc3\xa2rst\xc4\x83, Sex\n\xc8\x9aicleanu Maria, 20, femeiesc\nPa\xc8\x99cu Andrei, 30,
b\xc4\x83rb\xc4\x83tesc\xc8\x99al\xc4\x83u'
- deschiderea s-a făcut direct, conţinutul a rămas codat în UTF-8; nu îl decodăm deoarece nu
dorim să afişăm nimic din el, ci să adăugăm la el
>>> textdiac=u'...al doilea \u0219al\u0103u\n'
>>> print textdiac
...al doilea șalău
- textul ce va fi adăugat, cu Enter la sfârşit
>>> textdiacUTF=textdiac.encode('UTF-8')
- textul e convertit in UTF 8 pentru a putea fi salvat; dacă e salvat fără să fie codat, se obţine
eroare
>>> fisd.seek(0,2)
>>> fisd.write(textdiacUTF)
>>> fisd.flush()
>>> fisd.seek(0)
>>> fisd.read()
'\xef\xbb\xbfNume, V\xc3\xa2rst\xc4\x83, Sex\n\xc8\x9aicleanu Maria, 20, femeiesc\nPa\xc8\x99cu Andrei, 30,
b\xc4\x83rb\xc4\x83tesc\xc8\x99al\xc4\x83u...al doilea \xc8\x99al\xc4\x83u\n'
- se observă adăugarea la sfârşit, sau mai clar în Notepad:

Nume, Vârstă, Sex


Țicleanu Maria, 20, femeiesc
Pașcu Andrei, 30, bărbătescșalău...al doilea șalău

Dacă fişierul are structura CSV se procedează la fel, realizând codarea UTF înainte de scrierea
datelor:
>>> fisd=open(r'Exemple\diacritice.txt','a+')
- fişierul s-a deschis în modul append-read pentru a scrie automat la sfârşit
>>> fisCSV=csv.writer(fisd,delimiter=':',lineterminator='...\n')
- s-a definit un dialect CSV cu : în loc de virgulă şi puncte de suspensie la final de linii
>>> matriceUnicode=[[u'\u0219al\u0103u' for i in range(3)] for j in range(3)]
>>> matriceUnicode
[[u'\u0219al\u0103u', u'\u0219al\u0103u', u'\u0219al\u0103u'],
[u'\u0219al\u0103u', u'\u0219al\u0103u', u'\u0219al\u0103u'],
[u'\u0219al\u0103u', u'\u0219al\u0103u', u'\u0219al\u0103u']]
- am generat o matrice care să conţină cuvântul şalău de 3x3 ori; încercarea de a scrie aceste
date prin CSV eşuează
>>> matriceUTF=[[x.encode('UTF-8') for x in rand] for rand in matriceUnicode]
- am convertit matricea în UTF-8
(sau, prin codecs:
import codecs (dacă nu era importat)
matriceUTF=[list(codecs.iterencode(rand,'UTF-8')) for rand in matriceUnicode] )
>>> fisCSV.writerows(matriceUTF)
- am scris matricea la finalul fişierului
>>> fisd.flush()
Fișiere cu diacritice 24

>>> fisd.seek(0)
>>> fisd.readlines()
['\xef\xbb\xbfNume, V\xc3\xa2rst\xc4\x83, Sex\n','\xc8\x9aicleanu Maria, 20, femeiesc\n', 'Pa\xc8\x99cu Andrei, 30,
b\xc4\x83rb\xc4\x83tesc\xc8\x99al\xc4\x83u...al doilea \xc8\x99al\xc4\x83u\n',
'\xc8\x99al\xc4\x83u:\xc8\x99al\xc4\x83u:\xc8\x99al\xc4\x83u...\n', '\xc8\x99al\xc4\x83u:\xc8\x99al\xc4\x83u:\xc8\x99al\xc4\x83u...\n',
'\xc8\x99al\xc4\x83u:\xc8\x99al\xc4\x83u:\xc8\x99al\xc4\x83u...\n']

În Notepad:
Nume, Vârstă, Sex
Țicleanu Maria, 20, femeiesc
Pașcu Andrei, 30, bărbătescșalău...al doilea șalău
șalău:șalău:șalău...
șalău:șalău:șalău...
șalău:șalău:șalău...
- am boldat datele adăugate ultima dată

Concluzii:
 diacriticele trebuie să fie în format UTF-8 când sunt pe disc (în fişiere);
 diacriticele trebuie să fie în format Unicode când sunt în RAM (în timpul procesării, în timpul
afişării)
Pentru a conserva diacriticele în fișiere accesate din Web (presupunând că acestea sunt livrate
în format UTF-8), programele Python trebuie:
 să convertească conținutul fișierelor în Unicode cât mai devreme cu putință:
o imediat după deschiderea simplă cu open() sau…
o …dacă se dorește accesare prin CSV, imediat după accesarea datelor cu csv.reader();
 să reconvertească din Unicode în UTF-8 cât mai târziu cu putință:
o chiar înainte de scrierea datelor în fișier cu write,writelines sau…
o …dacă scrierea se face prin CSV, chiar înainte de executarea writerow, writerows, etc.
Dacă fişierul se dechide cu codecs.open în loc de open, nu mai e necesară conversia, fiind
asigurată atât la citire cât şi la scriere (dar astfel de fişiere nu pot fi accesate prin CSV).
25 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

2.4 Foldere şi interfaţa cu sistemul de operare


În plus faţă de funcţia open pentru acces direct la fişiere, Python poate comunica cu sistemele
de operare populare prin intermediul modulelor os (operaţii cu foldere, fişiere, drepturi), os.path
(informaţii despre căi, foldere şi fişiere), shutil (operaţii mai complexe – copiere de arbori, arhivare) şi
tempfile (gestiunea fişierelor temporare).
Majoritatea funcţiilor sunt proiectate pentru sisteme Unix, dar operaţiile de bază funcţionează şi
pe Windows.
>>> import os
>>> os.name
'nt'
- identificatorul interfeţei OS; pe sisteme Windows e vorba de interfaţa NT, denumire rămasă de
la Windows NT
>>> os.environ
- se vor afişa variabilele de mediu sub formă de dicţionar; nu le detaliem aici, diferă de la un
calculator la altul dar printre ele se vor putea găsi PYTHONPATH, numele calculatorului, calea Windows
etc.; fiind vorba de un dicţionar, se poate accesa oricare din variabile dacă i se cunoaşte numele, ca mai
jos:
>>> os.environ['PYTHONPATH']
'C:\\Python27\\Exemple'
- mai departe, aceste variabile pot fi modificate sau şterse cu os.putenv sau os.unsetenv, dar
recomandăm evitarea acestei operaţii deoarece apar riscuri legate de buna funcţionare a sistemului
>>> os.getcwd()
'C:\\Python27'
- afişează folderul curent, la care se raportează toate căile de acces la fişiere
>>> os.chdir(r'Exemple')
>>> os.getcwd()
'C:\\Python27\\Exemple'
- chdir asigură deplasarea într-un folder nou, relativ la cel curent
>>> os.listdir('.')
['arhiva.zip', 'criterii.py', 'diacritice.txt', 'fisier.csv', 'fisier1 - Copy.txt', 'fisier1.txt', 'fisier2.txt', 'fisier3.txt', 'fisierAltiDelimit.txt', 'fisiercuunicod.txt',
'fisierdiacritice.txt', 'fisierExcelCSV.csv', 'fisierExcelCSV2.csv', 'fisierExcelTAB.txt', 'fisiernou.csv', 'folder1', 'functii.py', 'functii.pyc',
'generatori.py', 'generatori.pyc', 'moddiacr.py', 'moddiacr.pyc', 'modul.py', 'modul.pyc', 'scriediac.py', 'scriediac.pyc']
- se afişează conţinutul folderului curent, simbolizat prin punct; acelaşi rezultat se obţinea dacă
în loc de punct tastam calea folderului
>>> os.access(r'C:\Python27\Exemple',os.F_OK)
True
- verifică existenţa unui folder, dacă al doilea argument e os.F_OK; pentru al doilea argument se
mai pot folosi R_OK,W_OK,X_OK pentru a testa existenţa drepturilor read,write,execute asupra căii
>>> os.mkdir('foldernou')
>>> os.chdir('foldernou')
>>> os.getcwd()
'C:\\Python27\\Exemple\\foldernou'
- mkdir() creează un folder nou în folderul curent; se poate verifica apariţia folderului pe disc sau
prin intrarea în el cu chdir()
>>> os.makedirs(r'subarbore\subfolder\folder')
- creează un întreg lanţ de foldere
>>> os.removedirs(r'subarbore\subfolder\folder')
- şterge un lanţ de foldere, cu condiţia să fie goale; dacă nu sunt goale, se vor folosi os.remove
pentru a şterge fişiere şi os.rmdir pentru a şterge foldere unul câte unul
>>> os.getcwd()
'C:\\Python27\\Exemple\\foldernou'
>>> os.chdir('..')
- am trecut în folderul părinte pentru a putea efectua operaţii pe foldernou
Foldere şi interfaţa cu sistemul de operare 26

>>> os.getcwd()
'C:\\Python27\\Exemple'
>>> os.rename('foldernou','altfolder')
- s-a schimbat numele lui foldernou; operaţia funcţionează identic pentru fişiere
>>> fisier=open(r'altfolder\fis.txt','w+')
>>> fisier.write('xxxx')
>>> fisier.close()
>>> os.rename(r'altfolder\fis.txt',r'altfolder\fis2.csv')
- s-a creat un fişier al cărui nume a fost apoi schimbat; există şi funcţia renames() care schimbă
denumirea unui lanţ de foldere

Observații: Fiind vorba de o interfaţă cu sistemul de operare, modulul os asigură şi accesul la fişiere dar
la un nivel mai scăzut (mai primitiv şi în mod binar) decât funcţia open(). Funcţii ca os.open(), os.fdopen()
asigură deschiderea de fişiere, os.read() şi os.write() asigură operaţii la nivel de octet.

>>> os.rmdir('altfolder')
- eroare! folderele trebuie să fie goale pt. a fi şterse
>>> os.remove(r'altfolder\fis2.csv')
- s-a şters fişierul
>>> os.rmdir('altfolder')
- s-a şters folderul
>>> os.tempnam()
Warning (from warnings module):
File "__main__", line 1
RuntimeWarning: tempnam is a potential security risk to your program
'C:\\Users\\2\\AppData\\Local\\Temp\\2'
- se afişează calea folderului rezervat pentru fişiere temporare; fişierele temporare se creează şi
gestionează cu modulul tempfiles
>>> os.mkdir('foldernou')
>>> fisier=open(r'foldernou\fis.html','w+')
>>> fisier.write('<font color=red>xxxx</font>')
>>> fisier.close()
- s-a creat un fişier HTML
>>> os.startfile(r'foldernou\fis.html')
- startfile() e echivalentul unui dublu clic pe fişier, ar trebui să deschidă fişierul în browser
>>> os.startfile(r'foldernou\fis.html','edit')
>>> os.startfile(r'foldernou\fis.html','print')
- dacă al doilea argument e edit sau print se execută programul responsabil cu editarea sau
printarea fişierului
>>> os.startfile(r'foldernou','explore')
>>> os.startfile(r'foldernou','find')
- dacă folosim startfile() pe foldere, se pot executa operaţiile explore şi search pe folder
>>> os.system('cmd')
- funcţia system transferă argumentul spre sistemul de operare, care îl va interpreta ca pe o
comandă directă, în genul celor care se tastează în caseta Run din Windows; comanda cmd va lansa în
execuţia linia de comandă Windows

Următorii indicatori oferă informaţii despre cum trebuie scrise căile în Python (acestea diferă de
la un sistem de operare la altul). Rezultatele aici indicate sunt pentru Windows:
>>> os.curdir
'.'
- simbolul folosit în loc de cale pentru a indica folderul curent
>>> os.pardir
'..'
27 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

- simbolul folosit pentru a indica folderul părinte


>>> os.sep
'\\'
- simbolul folosit ca separator de foldere în căi; după cum am arătat, dublarea nu mai e necesară
dacă prefixăm căile cu un r
>>> os.extsep
'.'
- simbolul folosit ca separator între numele fişierului şi extensie

O funcţie puternică oferită de os este walk(), pentru parcurgerea recursivă a unui întreg arbore
de foldere:

>>> os.chdir('foldernou')
>>> os.makedirs(r'folder1\folder11\folder111')
>>> os.makedirs(r'folder1\folder11\folder112')
>>> os.makedirs(r'folder1\folder12\folder121')
- în foldernou am creat un arbore de 6 foldere, împărţite pe 3 nivele
>>> f1=open(r'folder1\f1.txt','w+')
>>> f12=open(r'folder1\folder12\f12.txt','w+')
>>> f121=open(r'folder1\folder12\folder121\f121.txt','w+')
>>> f111=open(r'folder1\folder11\folder111\f111.txt','w+')
- am creat 4 fişiere în diverse locuri din arbore
>>> [x for x in os.walk('folder1')]
[('folder1', ['folder11', 'folder12'], ['f1.txt']),
('folder1\\folder11', ['folder111', 'folder112'], []),
('folder1\\folder11\\folder111', [], ['f111.txt']),
('folder1\\folder11\\folder112', [], []),
('folder1\\folder12', ['folder121'], ['f12.txt']),
('folder1\\folder12\\folder121', [], ['f121.txt'])]

Se remarcă returnarea unei liste de tuple cu 3 elemente:


 primul e calea fiecărui folder din arbore (boldată)
 al doilea e lista folderelor din fiecare folder (italic);
 al treilea este lista fişierelor din fiecare folder.
Se observă că parcurgerea a fost top down, începând de la folder1 (argumentul cu care s-a
apelat walk!). E posibil să se facă şi parcurgere bottom up:
>>> [x for x in os.walk('folder1',topdown=False)]
[('folder1\\folder11\\folder111', [], ['f111.txt']),
('folder1\\folder11\\folder112', [], []),
('folder1\\folder11', ['folder111', 'folder112'], []),
('folder1\\folder12\\folder121', [], ['f121.txt']),
('folder1\\folder12', ['folder121'], ['f12.txt']),
('folder1', ['folder11', 'folder12'], ['f1.txt'])]

Principala utilizare a funcţiei walk() apare în ciclurile For care execută o operaţie pentru fiecare
tuplu găsit de walk(). Aceste operaţii pot fi chiar de ştergere/redenumire/modificare caz în care
următorul tuplu accesat de walk va REFLECTA modificările făcute la precedentul pas. În exemplul
următor parcurgem arborele din folder1 şi modificăm extensia la toate fişierele din txt în csv:

>>> f1.close()
>>> f12.close()
>>> f121.close()
>>> f111.close()
Foldere şi interfaţa cu sistemul de operare 28

- închidem fişierele rămase deschise încă de la creare, altfel nu avem permisiunea de a le


modifica pe disc
>>> for (folder,subfoldere,fisiere) in os.walk('folder1'):
for x in fisiere:
calefisier=folder+'\\'+x
pozitiepunct=x.rfind('.')
numenou=folder+'\\'+x[0:pozitiepunct+1]+'csv'
os.rename(calefisier,numenou)

Explicații:
 pentru fiecare fişier i s-a obţinut calea relativă concatenând primul şi al treilea element din tuplul
curent returnat de walk(); reamintim că primul e calea folderului la care a ajuns walk, iar al
treilea e lista numelor fişierelor (parcursă cu for x in fisiere);
 pentru fiecare fişier s-a detectat poziţia ULTIMULUI punct, pentru cazurile în care ar putea exista
mai multe puncte (rfind găseşte ultima apariţie a caracterului dorit, find o găseşte pe prima);
 noua cale a fişiereului s-a creat din calea veche, din care s-a decupat extensia cu x[0:pozitie+1]
(numele fişierului până la punct inclusiv) şi s-a adăugat noua extensie (csv);
 apoi s-a apelat rename pentru înlocuirea căii, care are ca efect schimbarea numelui fişierului.
Se poate verifica pe disc rezultatul operaţiei.

Într-o manieră similară se pot realiza orice transformări asupra folderelor şi fişierelor returnate
de walk, inclusiv ştergeri. În cazul ştergerilor, parcurgerea trebuie să fie bottom-up, deoarece trebuie
golit fiecare folder înainte de ştergerea sa.

>>> for (folder,subfoldere,fisiere)in os.walk('folder1',topdown=False):


for x in fisiere:
calefisier=folder+'\\'+x
os.remove(calefisier)
for x in subfoldere:
calesubfolder=folder+'\\'+x
os.rmdir(calesubfolder)

Se poate verifica faptul că folder1 a fost golit.

Aceste exemple funcţionează doar pe Windows, deoarece concatenarea între cale şi numele
fişierelor sau folderelor s-a făcut prin \\, acesta fiind separatorul acceptat de Python pt Windows.
Deoarece separatorul diferă de la un sistem de operare la altul, pentru ca programele să fie portabile,
căile trebuie construite prin intermediul modulului os.path ce asigură conversia automată a sintaxei
căilor după sistemul de operare.

>>> os.path.join('aaa','bbb')
'aaa\\bbb'
- asigură o concatenare pornind de la premisa că argumentele sunt componente ale unei căi;
funcţia oferă garanţia folosirii separatorului corect indiferent de sistemul de operare
>>> os.path.getsize('folder1')
0L
- indică dimensiunea folderului sau fişierului; 0L indică faptul că e gol; acesta e un test important
pentru a evita erorile la ştergerea de foldere
29 Lucrarea 2. Programarea în limbajul Python - lucrul cu fișiere și directoare

2.5 Bibliografie
D. M. Beazley, Python Essential Reference, Pearson Education, 2009
J. M. Zelle, Python programming: an introduction to computer science, Ed. Wilsonville: Franklin, Beedle,
2004
M.Lutz, Programming Python,O'Reilly, 2011.

S-ar putea să vă placă și