Updated Modifié

Le but de cette séance est de manipuler les fichiers (au sens large).

1. Rappels sur les Fichiers

1.1. Rappels sur les Formats

Un fichier peut utiliser de nombreux formats, sous Linux c’est la commande file qui peut déterminer le format indépendemment de l’extension. Faire cet exemple

$ echo "TOTO" > toto.mp3
$ file toto.mp3

Ouvrir votre éditeur favori et y écrire un petit texte de plusieurs lignes. Sauvegarder le fichier sous le nom exemple.txt, quel est le format de ce fichier ?

Expliquer le résultat de

$ hexdump -C exemple.txt

1.2. Manipulation sous “python“ : Lecture

Voici un premier exemple exemple.py de lecture de fichier

# Lecture (simple) ligne par ligne
with open("exemple.txt", "r") as fichier:
    for ligne in fichier:
        print(ligne.strip())

A quoi sert strip() ? Expliquer en quoi cela est une question de format.

1.3. Manipulation sous “python“ : Ajout

Modifier le code précédent pour ajouter une ligne en fin de fichier qui donne le nombre de lignes NN du fichier initialement.

Nombre de lignes: NN

On proposera deux solutions

  • en réouvrant le fichier pour l’ajout avec
    with open("exemple.txt", "a") as fichier:
    
  • en travaillant dans une seule boucle avec
    with open("exemple.txt", "r+") as fichier:
    

Expliquer les différences entre les deux solutions.

1.4. Manipulation sous “python“ : Lecture/Écriture

Écrire un programme elpmexe.py qui sauvegarde dans le fichier elpmexe.txt le contenu du fichier exemple.txt. On rappelle que les option pour open sont ici.

Quel est le coût en utilisation mémoire de votre solution, en fonction de la taille du fichier exemple.txt ?

2. Accès Bas Niveau

Il est parfois nécessaire d’utiliser des accès bas-niveau pour gérer certaines situation. Dans cette partie, nous allons voir les méthodes concernées.

2.1. Lecture Bas Niveau

On va utiliser la méthode os.open (donc ne pas oublier de faire import os). Nous avons ainsi accès au descripteur de fichier correspondant. Il faut ici cependant tout gérer, sans le mot-clef with.

fd = os.open("exemple.txt",os.O_RDONLY)
print(fd) # <fd>
print(os.getpid()) # <pid>
input("Appuyez sur Entrée")
octets = os.read(fd,1)
input("Appuyez sur Entrée")
octets = os.read(fd,1)
input("Appuyez sur Entrée")
octets = os.read(fd,1)
input("Appuyez sur Entrée")
os.close(fd)

Avant chaque “Entrée”, afficher les informations correspondantes depuis un (autre) shell

$ cat /proc/<pid>/fdinfo/<fd>

Que signifient les différents champs ?

2.2. Lecture Octet par Octet

Modifier le premier exemple en ajoutant une boucle pour lire toute une ligne octet par octet, ie jusque octets[0] == '\n'

Compléter ensuite en ajoutant un message message donnant le nombre de lignes NN du fichier initialement, ce message doit être affiché sur la sortie d’erreur standard avec os.write(2,message). Comment faire également avec la commande print ?

2.3. Lecture de fichier binaire

Il existe sur votre système des fichiers spéciaux, ie des interfaces en mode fichier avec le noyau. Commençons par le fichier /dev/random. Il faut l’ouvrir en mode binaire en ajoutant b:

with open("/dev/random", "rb") as fichier:

Reprendre exemple.py pour afficher le contenu ligne par ligne de /dev/random. Que constatez-vous ?

2.4. Application

Nous allons faire du chiffrement avec un masque jetable. Commencer par obtenir un fichier clef.bin de 1OMo d’octets aléatoires à partir de /dev/random.

Écrire un programme masque jetable qui prend comme paramètre un fichier à chiffrer et qui applique le masque jetable (le xor se fait avec ^ sur des objets bytes) en python). Le résultat est stocké dans le fichier out.bin (que se passe-t-il si l’on utilise la sortie standard ?)