Fitxers

Llegir i editar fitxers de text amb Python és molt senzill, fins i tot si venen comprimits. També veurem com controlar les possibles excepcions durant l'accés als fitxers.

Llegir, crear i editar fitxers de text de tot tipus (text pla, binaris, CSV, JSON, HTML… ) amb Python és ràpid i molt senzill.

Conceptes previs.

En el seu nucli, un fitxer és un conjunt contigu de bytes utilitzats per emmagatzemar dades.

Aquestes dades s’organitzen en un format específic i poden ser qualsevol cosa tan simple com un fitxer de text o tan complexe com un paquet ofimàtic o un videojoc.

Nosaltres ens centrarem en el primer cas, fixers en format de text pla perquè són simples, transparents i fàcils de manipular: es poden llegir amb qualsevol editor, processar amb scripts i entendre sense eines especials.

Els fitxers de de text pla es componen de tres parts principals:

  • Capçalera: metadades sobre el contingut del fitxer (nom del fitxer, mida, tipus, etc.)
  • Dades: contingut del fitxer tal com l’ha escrit el creador o editor
  • Final del fitxer (EOF): caràcter especial que indica el final del fitxer.

Rutes (Paths)

Quan accediu a un fitxer en un sistema operatiu, cal una ruta de fitxer.

La ruta del fitxer és una cadena que representa la ubicació d’un fitxer. Està dividit en tres grans parts:

  • Ruta de la carpeta: la ubicació de la carpeta de fitxers al sistema de fitxers on les carpetes posteriors estan separades per una barra inclinada / (Unix,Linux i MacOS) o una barra invertida \ (Windows)
  • Nom del fitxer: el nom real del fitxer
  • Extensió: el final de la ruta del fitxer amb un punt ( .) que s’utilitza per indicar el tipus de fitxer

Anem a analitzar un cas molt habitual; imaginem que volem obrir el fitxer animals.csv des del programa read_file.py.

/
├── python_files/ ← working directory
| │
| ├── data/ ← Current working directory (cwd)
| │ └── animals.csv
| │
| └── read_file.py ← Accessing this file

La ruta del fitxer correcta per tractar-lo en aquest cas, suposant que estem a Linux, seria:

file_path = "data/animals.csv"

És molt important l’ús de rutes de fitxers en aquest format, que es tracta d’una ruta relativa.

⚠Cal evitar sempre les rutes absolutes (per exemple: C:\Users\usuari1\file.py) o /home/user1 perquè només funcionen dins la màquina local i poden conduir a forats de seguretat.⚠


Modes d’accés a fitxers.

Per tal d’obrir un fitxer de text per treballar-hi; només cal usar la funció integrada open(), que només necessita la ruta del fitxer.

Apart de tenir el paràmetre necessari del file_name, informem el segon paràmetre, el mode, que pot tenir aquests valors:

  • ‘r’ Obert per llegir read (per defecte)
  • ‘w’ Obriu per escriure write, truncant (sobreescrivint) el fitxer primer si existeix.
  • ‘a’ Obriu per escriure append, si el fitxer existeix posa el contingut nou al final.
  • ‘rb’o’wb’ Obrir en mode binari (llegir/escriptura mitjançant dades de bytes)

També és important que sabeu que la paraula reservada with ens permet obrir el fitxer i tancar-lo automàticament.

⚠Avís: Recomanem usar `with open() per tal d’assegurar-vos que el fitxer es tanqui correctament i així evitar problemes!⚠

Exemple:

Per provar els exemples no et cal cap llibreria especial.

Crea un nou projecte amb uv.

Terminal window
uv init fitxers
cd fitxers

Ara, crea un fitxer anomenat demo.txt amb l’editor que vulguis.

O des del terminal si vols:

Terminal window
echo "Bon dia!" > demo.txt
``
Finalment, crea aquest programa en python.
```py
file_name = 'demo.txt'
with open(file_name, mode='r') as file:
print(file.read())
print(f"Fitxer {file_name} llegit correctament.")

El resultat que mostrarà és:

Terminal window
Bon dia!
Fitxer demo.txt llegit correctament.

A que és molt fàcil 🤓🤓 ?


Mode read, r

Fixem-nos de nou el bloc de codi que obre el fitxer, llegeix i mostra el seu contingut i el tanca quan ha acabat:

with open(file_name, mode='r') as file:
print(file.read())

Apart de tenir el paràmetre necessari del file_name, informem el segon paràmetre, el mode, que pot tenir aquests valors:

  • ‘r’ Obert per llegir read (per defecte)
  • ‘w’ Obriu per escriure write, truncant (sobreescrivint) el fitxer primer si existeix.
  • ‘x’ Obriu per escriure write un nou fitxer, si existeix el fixer retorna un error.
  • ‘a’ Obriu per escriure append, si el fitxer existeix posa el contingut nou al final.
  • ‘rb’o’wb’ Obrir en mode binari (llegir/escriptura mitjançant dades de bytes).

Mode append, a

Per exemple, si volem afegir una nova linia al final d’un fitxer existent, és tan senzill com usar aquest codi:

with open('data/demo.txt', 'a') as appender:
appender.write("\nHave a nice day!")

Mode crear, x

En aquest codi, si creem un fitxer que es digui my_file.txt (des de qualsevol editor) i provem aquest codi per crear-lo i escriure damunt ens retornarà un error 🤔.

f = open("my_file.txt", mode="x")

Error que retorna:

Terminal window
Traceback (most recent call last):
File "/home/miquel/Documents/pyprova/demo2.py", line 1, in <module>
f = open("my_file.txt", mode="x")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileExistsError: [Errno 17] File exists: 'my_file.txt'

Mode write, w (sobreescriptura)

En canvi, si volem crear un nou fitxer que no tenim, o sobreescriure un existent, usem el mode='w'.

file_name = 'species.txt'
# Obrim el fitxer en mode escriptura.
with open(file_name, mode='w') as file:
file.write("Homo sapiens\n")
file.write("Mus musculus\n")
file.write("Drosophila melanogaster\n")
print(f"Fitxer '{file_name}' escrit correctament.")

**No oblidis obrir el fitxer amb el teu editor preferit (VSCOde, Pycharm, SublimeText…) per garantir que el programa ha funcionat!

Si volem escriure els continguts d’una llista, una línia cada element, ho podem aconseguir amb aquest codi:

file_name = 'sequences.txt'
# Llista de seqüències d'ADN.
sequences = [
"ATGCGTAACG",
"CGTACGTAGC",
"TTAGGCATTA"
]
# Obrim el fitxer en mode 'w' (escriptura). Si ja existeix, es sobreescriurà!
with open(file_name, mode='w') as file:
for seq in sequences:
file.write(seq + '\n') # Cada seqüència a una nova línia
print(f"Fitxer '{file_name}' creat i seqüències escrites correctament.")

Així, hem aconseguit escriure una sèrie de seqüències d’ADN, una per línia.


Encoding

Tingueu en compte que el format de codificació de caràcters del fitxer ha de ser l’adient per evitar errors.

Per exemple, si s’ha creat un fitxer amb la codificació UTF-8 (Unicode), amb millions de caràcters i intenteu analitzar-lo amb la codificació ASCII (128 caràcters), si hi ha un caràcter que es troba fora d’aquests 128 valors, es generarà un error.

També heu de tenir en compte Windows utilitza els caràcters \r\n per indicar una línia nova, mentre que Unix, Linux i les versions més noves de Mac utilitzen només el caràcter \n. Això pot provocar algunes complicacions quan esteu processant fitxers en un sistema operatiu diferent de l’origen del fitxer.

Anem a veure un exemple senzill.

Si a demo.txt tenim aquest contingut:

Com esteu?
Espero que molt bé!

Hem de tenir en compte un altre paràmetre: encoding="UTF-8"

with open('demo.txt', mode='r', encoding="UTF-8") as reader:
# Further file processing goes here
# Read & print the entire file
print(reader.read())
print("Fitxer demo.txt llegit correctament.")

Altrament, el programa no interpretarà els accents ni altres caràcters é :

Com esteu?
Espero que molt bé!
Fitxer demo.txt llegit correctament.

Exercicis.

Per a completar els exercicis, primer crea el directori py_files i crea el fitxer oferta1.txt.

Ho pots fer amb entorn gràfic o per terminal.

/
├── py_files/ ← working directory
| │
| ├── oferta1.txt
| │
| └── exercicis_files.py

Per terminal ho pots fer així:

Terminal window
mkdir py_files
cd py_files

El contingut del fitxer oferta1.txt ha de ser:

Terminal window
Python
Pandas
Matplotlib
FastAPI
3dJS
React
MariaDB
Activitat

1.- Fes un programa que obri el fitxer, el llegeixi i mostri tot el seu contingut per pantalla.

Activitat

2.- Crea un altre programa que obri el fitxer i a la última línia escrigui el següent text i icona. Revisa l’encoding.

Oferta revisada ✅
Activitat

3.- Crea un programa que guardi en un fitxer uns compostos químics que hem guardat en un diccionari, un per línia:

file_name = 'compostos.txt'
# Diccionari amb compostos químics i les seves fórmules
compostos = {
"Aigua": "H2O",
"Diòxid de carboni": "CO2",
"Metà": "CH4",
"Etanol": "C2H5OH"
}

En aquest cas, hauriem de tenir 4 línies de fitxer. La primera hauria de contenir:

Terminal window
Aigua: H2O

Tractament d’excepcions en fitxers.

Les excepcions a Python són una eina molt potent que la gran majoria de llenguatges de programació moderns tenen. Es tracta d’una manera de controlar el comportament d’un programa quan es produeix un error.

Això és molt important ja que sinó tractem aquest error, el programa es pararà, i això no és una opció vàlida quan publiquem la nostra aplicació.

Tenim el llistat d’excepcions al web oficial Pyhton.

Python descomposa el codi que pot donar exepcions en en 3 blocs, i un d’opcional:

  • try és el tros de codi que preveiem que pot donar excepcions.
  • except el trps de codi que tracta excepció. En el cas de l’accés a fitxers tenim aquests casos: ZeroDivisionError i ValueError.
  • finally és un tros de codi que s’executarà sempre, tant si tot ha funcionat com si hi ha hagut algun error. En aquest cas no el necessitem.
  • else si després d’executar el try volem executar més operacions només si ha funcionat podem usar l’else.

Per a què funcioni el bloc try hi ha d’haver o un except o un finally (sinó, no té sentit crear un try).

En resum:

Anem a veure com funciona a la pràctica.

Si volem llegir un fitxer amb el nom de 6 bioinformàtiques i els descobriments que han aportat a la humanitat; i guardar-les en una llista.

bioinformatiques1.csv

Nom,Descobriment Famos,Dates
Anna Smith,Desenvolupament d'algoritmes per anàlisi de seqüències d'ADN,1960-
Maria Garcia,Creació de bases de dades genòmiques,1955-
Laura Johnson,Innovacions en anàlisi de xarxes gèniques,1965-
Patricia Brown,Desenvolupament de models predictius en bioinformàtica,1970-
Linda Davis,Contribucions a l'anàlisi de dades d'expressió gènica,1945-2010
Barbara Martinez,Investigació en genòmica del càncer,1968-

Les possibles excepcions que preveiem seran:

  • FileNotFoundError —> No trobem el fitxer.
  • OSError —> Que l’usuari no té permisos per accedir-hi.
  • Exception —> Aquesta excepció captura qualsevol altre error que pugui sorgir. És bona pràctica incloure-la per poder depurar i cercar solucions.

En els 2 últims casos el que fem és mostrar la traça de l’excepció complerta, per exemple: except Exception as e

El codi final serà:

fitxer = 'bioinformatiques1.csv'
# Llista per guardar les línies del fitxer
linies = []
try:
with open(fitxer, mode='r', encoding='utf-8') as file:
# Ometre la primera fila (encapçalats)
next(file)
for line in file:
try:
# El separador de cada dada del fitxer és ','
nom, descobriment, dates = line.strip().split(',')
linies.append((nom, descobriment, dates))
except ValueError as e:
print(f"Error al processar la línia: {line.strip()}. Error: {e}")
exit() #Forcem la sortida del programa.
except FileNotFoundError:
print(f"El fitxer {fitxer} no s'ha trobat.")
except OSError as e:
print(f"S'ha produït un error del sistema en intentar accedir al fitxer: {e}")
except Exception as e:
print(f"S'ha produït un error inesperat: {e}")
else:
print(f"Hem aconseguit llegir tot el fitxer correctament!")
print(linies[0])
finally:
print("Gràcies per confiar en el nostre programari.")

Provem de llegir el fitxer; primer amb el nom exacte.

file_path = Path('bioinformatiques1.csv')
Terminal window
app-py3.10miquel@LAPTOP-NBA651HM:/mnt/c/Users/USUARI/m14/s6$ python files-bio-exceptions.py
Hem aconseguit llegir tot el fitxer correctament!
('Anna Smith', "Desenvolupament d'algoritmes per anàlisi de seqüències d'ADN", '1960-')
Gràcies per confiar en el nostre programari.

Si ha funcionat, ara provarem de forçar una excepció per veure si es captura correctament: canviem el nom del fitxer a llegir per un que no existeixi i executem de nou el codi.

file_path = Path('bioinformatics-none.csv')

Com que no tenim el fitxer, ens surt la excepció que hem programat. Així podem aportar una millor experiència als/les usuaris/es de l’aplicació.

Terminal window
app-py3.10miquel@LAPTOP-NBA651HM:/mnt/c/Users/USUARI/m14/s6$ python files-bio-exceptions.py
El fitxer bioinformatiques1123.csv no s'ha trobat.
Gràcies per confiar en el nostre programari.

Una altra excepció que podem tenir és que alguna de les línies no tingui el separador correcte, que en el nostre cas és la ,. Podem provar a veure si ho detecta adequadament, canviant la , per uns espais :

Nom,Descobriment Famos,Dates
Anna Smith Desenvolupament d'algoritmes per anàlisi de seqüències d'ADN,1960-
Maria Garcia,Creació de bases de dades genòmiques,1955-

Si executem el codi ens surt l’error que hem esperat. Hem forçat de sortir del programa amb la instrucció exit(), però en programes en producció no hauriem de usar exit()

Terminal window
app-py3.10miquel@LAPTOP-NBA651HM:/mnt/c/Users/USUARI/m14/s6$ python files-bio-exceptions.py
Error al processar la línia: Anna SmithxdDesenvolupament d'algoritmes per anàlisi de seqüències d'ADN,1960-. Error: not enough values to unpack (expected 3, got 2)
Gràcies per confiar en el nostre programari.

Referències consultades: