# TD2 : Fichiers et exceptions
## Partie 1 :
**Informations importantes sur les fichiers :**
- Pour lire ou écrire un fichier, on doit d'abord l'ouvrir en spécifiant le chemin :
```python
fichier = open("text.txt") # dans le dossier du script
fichier = open("C:/Bureau/text.txt") # chemin complet
```
- Pour choisir le mode d'ouverture, plusieurs codes sont disponibles :
- `r` en lecture
- `w` en écriture (**supprime le contenu présent**)
- `a` en écriture **après** le contenu déjà présent
- `b` en mode binaire sans interprétation des données du fichier (pour des fichiers non-texte)
```python
f_lecture = open("text.txt", "r")
f_ajout = open("text.txt", "a")
f_ecriture_binaire = open("binaire.bin", "wb")
```
- Pour lire et écrire dans le fichier, les méthodes `write()`, `readline()` et `readlines()` sont disponibles :
```python
fichier = open("text.txt", "ra") # lecture + écriture
# (1) Lire tout le fichier d'un coup
contenu = fichier.readlines()
# (2) Lire ligne par ligne
for i in range(20): # 20 premières lignes
ligne = fichier.readline()
# Écriture
fichier.write("bip boup\n")
fichier. close()
```
### GPS 1
Données :
```
152902.00 4823.409837 00425.903959 66.804
152903.00 4823.409837 00425.903959 66.804
152904.00 4823.409837 00425.903959 66.804
152905.00 4823.409837 00425.903959 66.804
152906.00 4823.409837 00425.903959 66.804
152907.00 4823.409837 00425.903959 66.804
...
```
Programme :
- *Ouvrir* `GPS_data.txt`
- *Lire* les 50 premières lignes
- *Convertir* les données en flottants
- *Stocker* le contenu dans une liste
- *Afficher* la première ligne, première colonne
- *Écrire* les données dans un autre fichier (`' '` entre chaque nombre, `'\n'` en fin de ligne)
**Solution :**
```python
def gps_1():
# Ouverture du fichier en lecture
in_file = open('GPS_data.txt', 'r')
# Création et remplissage de la liste des données
data = []
for i in range(50):
line = in_file.readline() # Lecture
# Séparation de la chaine sur chaque espace
data_txt = line.split(' ')
# Conversion en float
data_float = [float(x) for x in data_txt]
data.append(data_float) # Ajout
# Fermeture du fichier
in_file.close()
# Premiere ligne
print(data[0])
# Premiere colonne
print([line[0] for line in data])
# print(np.array(data)[:, 0])
# Écriture du fichier de sortie
out_file = open('GPS_data_extract.txt', 'w')
for line in data:
line_txt = ' '.join([str(elt) for elt in line]) + '\n'
out_file.write(line_txt)
out_file.close()
```
### GPS 2
Les exceptions permettent de gérer des erreurs et d'adapter le comportement du programme en fonction de celles-ci :
```python
try:
...
# Code erroné ou pouvant lever des exceptions
...
except TypeError:
print("Mauvais type")
except NameError:
print("Mauvais nom")
except Exception: # Exception est le type par défaut
print("Autre erreur")
```
Programme :
- Même chose sur la totalité du fichier
- Gérer les erreurs avec des **exceptions**
**Solution :**
```python
def gps_2():
# Ouverture du fichier en lecture
in_file = open('GPS_data.txt', 'r')
# Création et remplissage de la liste des données
data = []
for line in in_file:
# Séparation de la chaine sur chaque espace
data_txt = line.split(' ')
# Conversion en float
try:
data_float = [float(x) for x in data_txt]
data.append(data_float) # Ajout
except Exception:
print("Données non conformes: ", line)
# Fermeture du fichier
in_file.close()
...
```
### GPS 3
Le fichier GPS stocke les quatre éléments suivants : **temps**, **latitude**, **longitude** et **altitude**. Les latitudes et longitudes ont un format particulier qu'il faut convertir :
```
48|23|.|409837
degré|minutes|.|décimales de minute
en
48|.|39016395
degré|.|décimales de degré
```
Pour vous aider :
- `//` est une division entière
- `%` est le reste de la division entière
- Une minute de degré est 1/60e de degré
**Solution :**
```python
def minutes_vers_degres(degres_minutes):
# Extraction des deux premiers chiffres
degres = degres_minutes // 100
# Extraction des deux derniers chiffres
minutes = degres_minutes % 100
return degres + minutes/60.0
```
Pour vous aider pour la suite :
- Pour convertir `lat` et `long` en UTM, utilisez la fonction `LLUC.LLtoUTM(23, lat, -long)`. Cette fonction renvoie la zone, le "Easting" et le "Northing".
- La fonction `np.mean()` permet de calculer la moyenne.
- Enfin, vous pouvez afficher vos données avec `matplotlib` :
```python
import mpl_toolkits.mplot3d.axes3d as p3
# Conversion en array numpy
coords = np.array(coords)
fig = plt.figure()
ax = p3.Axes3D(fig)
# plot3D a besoin de tableau 1 dimension
ax.plot3D(coords[:, 1], coords[:, 2], coords[:, 3])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.add_axes(ax)
plt.show()
```
**Solution :**
```python
...
with open('GPS_data.txt', 'r') as in_file:
coords = []
for line in in_file:
data_txt = line.split()
try:
data_float = [float(x) for x in data_txt]
lat = minutes_vers_degres(data_float[1])
longi = minutes_vers_degres(data_float[2])
(zone, e, n) = LLUC.LLtoUTM(23, lat, -longi)
coords.append([data_float[0], e, n, data_float[3]])
except Exception:
print("Données non conformes : ", line)
...
```
### GPS 4
`re` est une bibliothèque d'expression régulière qui permettent de créer des modèles intelligents de validation de données. On veut s'en servir pour définir une ligne correcte :
```
nombre.nombre nombre.nombre nombre.nombre nombre.nombre
temps latitude longitude altitude
```
Utilisez le site [regex101](https://regex101.com/) pour tester votre expression régulière.
**Solution :**
```
(\d+\.\d+\ ){3}(\d+\.\d+$)
```
Une expression régulière s'utilise comme suit :
```python
expr_reg = re.compile('he[a-zA-Z]+') # mot qui a pour suffixe 'he'
expr_reg.match('hello') # True
expr_reg.match('bipboup') # False
expr_reg.match('herd') # True
```
**Solution :**
```python
expr_reg = re.compile(r'\d+\.\d+ \d+\.\d+ \d+\.\d+ \d+\.\d+$')
with open('GPS_data.txt', 'r') as in_file:
coords = []
for line in in_file:
if expr_reg.match(line):
data_txt = line.split()
data_float = [float(x) for x in data_txt]
lat = minutes_vers_degres(data_float[1])
longi = minutes_vers_degres(data_float[2])
(zone, e, n) = LLUC.LLtoUTM(23, lat, -longi)
coords.append([datal[0], e, n, datal[3]])
...
```
### GPS 5
Il faut cette fois-ci extraire les données des données brutes :
```
$GPGGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1 = UTC of Position
2 = Latitude
3 = N or S
4 = Longitude
5 = E or W
6 = GPS quality indicator (0=invalid;1=GPS fix;2=Diff. GPS fix)
7 = Number of satellites in use [not those in view]
8 = Horizontal dilution of position
9 = Antenna altitude above/below mean sea level (geoid)
10 = Meters (Antenna height unit)
11 = Geoidal separation (Diff. between WGS-84 earth ellipsoid and mean sea level. -=geoid is below WGS-84 ellipsoid)
12 = Meters (Units of geoidal separation)
13 = Age in seconds since last update from diff. reference station
14 = Diff. reference station ID#
15 = Checksum
```
**Solution :**
```python
with open('GPS_data_raw.txt', 'r') as in_file:
coords = []
for line in in_file:
data_txt = line.split(',')
try:
data_float = [float(data_txt[i]) for i in [2, 4, 9]]
lat =minutes_vers_degres(data_float[0])
if data_txt[3] == 'S':
lat = -lat
longi = minutes_vers_degres(data_float[1])
if data_txt[5] == 'W':
longi = -longi
(zone, e, n) = LLUC.LLtoUTM(23, lat, longi)
coords.append([data_float[0], e, n, data_float[2]])
except Exception:
print("Données non conformes : ", line)
```
### Heavy Weather
Un nouveau format vous est présenté mais il correspond à un type de données que votre ordinateur ne sait pas déchiffrer naturellement. On possède cependant des informations sur son chiffrement :
```txt
Each row of data is stored in 56 byte chunks starting from the beginning of
the file (no header).
ROW
OFFSET Type Name Unit
------ --------- ---------------- -----
00 Double [8] Timestamp days from 12/30/1899 00:00:00 (GMT)
08 Float [4] Abs Pressure hectopascals (millibars)
12 Float [4] Relative Pressure hectopascals (millibars)
16 Float [4] Wind Speed meters/second
20 ULong [4] Wind Direction see below
24 Float [4] Wind Gust meters/second
28 Float [4] Total Rainfall millimeters
32 Float [4] New Rainfall millimeters
36 Float [4] Indoor Temp celsius
40 Float [4] Outdoor Temp celsius
44 Float [4] Indoor Humidity %
48 Float [4] Outdoor Humidity %
52 ULong [4] unknown - (Value is always 0)
```
Pour lire des fichiers binaires :
```python
f = open("binary.bin", "rb")
f.read(8) # Pour lire les 8 premiers bits
```
La librairie `struct` permet de déchiffrer les structures du langage C en Python :
```python
import struct
s = struct.Struct('<hhf') # short short float
record = f.read(8)
fields = s.unpack()
record2 = s.pack(*fields) # l'étoile met la list 'à plat'
```
Programme :
- Lire le fichier chunk par chunk
- Corriger la température extérieure de 1.4 degrés Celsius
**Solution :**
```python
with open('binary_data.dat', 'rb') as src, \
open('binary_data_checked.dat', 'wb') as dst:
try:
# Record struct format
s = struct.Struct('<d3fL7fL')
while True:
# Read a record from source file
record = src.read(56)
if len(record) != 56:
break
# Adjust data
fields = s.unpack(record)
adjusted = list(v+1.4 if i == 9 else v for i, v in enumerate(fields))
# Encode the record and write it to the dest file
record = s.pack(*adjusted)
dst.write(record)
except Exception:
# Your error handling here
# Nothing for this example
pass
```
###### tags: `python`