Les entrées sorties

Que contient un fichier ?

Le dossier de travail contient un fichier mona-lisa.jpeg.

In [ ]:
!ls -l
total 1368
drwxr-xr-x 3 denis denis    4096 sept. 14  2015 data
-rw-r--r-- 1 denis denis      26 sept.  7  2015 essai2.txt
-rw-rw-r-- 1 denis denis       7 août  28 15:05 essai3.txt
-rw-rw-r-- 1 denis denis       8 août  28 14:55 essai.txt
-rw-r--r-- 1 denis denis  163275 août  29 13:52 les-entrees-sorties-master.ipynb
-rw-r--r-- 1 denis denis   13451 sept.  7  2015 mona-lisa.jpeg
-rw-r--r-- 1 denis denis 1200420 août  28 20:49 temp.ipynb

Le suffixe .jpeg suggère qu'il s'agit d'un fichier qui contient une image. On peut tenter de lire le fichier avec un programme approprié. Il existe un module Python qui permet de d'afficher une image à l'intérieur d'un notebook :

In [ ]:
from IPython.display import Image
Image("mona-lisa.jpeg")
Out[ ]:

Nous avons bien la confirmation que le fichier contient une image. Plus précisément, il contient un texte qui représente l'image de Mona Lisa. Pour interpréter correctement ce texte, il faut connaitre les règles de codage. Ici, le suffixe .jpeg donné au nom du fichier nous renseigne sur ce codage. Il s'agit du format d'image compressée JPEG.

Le programme cat ne permet pas de visualiser correctement le texte au format JPEG :

In [ ]:
!cat mona-lisa.jpeg | head
����JFIF��C		
2!!22222222222222222222222222222222222222222222222222��"��	
���}!1AQa"q2���#B��R��$3br�	
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz���������������������������������������������������������������������������	
���w!1AQaq"2�B����	#3R�br�
$4�%�&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz��������������������������������������������������������������������������?o�K	H\�Nh`�=�)ݜ���0�-��=iJ1�F*�;��݀�CH�@c4���\)Pzɧ̊�1)���9����	B�l0S�q�sM��
J���*�Aڏ`�z,W11!|�VL�<��()?Ҽ�R
9S���q��ԺwPF�ML�6;�D2��QȠ]���>����K�Ml�Ml�kvwXO5bbyК�Ғ��$%�P{
U��&rN5)FR0�A9���_/0}:��l���JH�O4�ȤDH��>��R�c�`�R�j`H^F����P'束��i�$H���88��(�9覓`A(brGq��ت�UrN�NNyϯ�Xu)0
A!�gҡ!0F���J�4�ܱpH�

Le format JPEG est un format binaire, c'est-à-dire un texte écrit avec les caractères bit 0 et 1. On peut le lire avec le programme xxd :

In [ ]:
!xxd -b mona-lisa.jpeg | head
00000000: 11111111 11011000 11111111 11100000 00000000 00010000  ......
00000006: 01001010 01000110 01001001 01000110 00000000 00000001  JFIF..
0000000c: 00000001 00000000 00000000 00000001 00000000 00000001  ......
00000012: 00000000 00000000 11111111 11011011 00000000 01000011  .....C
00000018: 00000000 00001000 00000110 00000110 00000111 00000110  ......
0000001e: 00000101 00001000 00000111 00000111 00000111 00001001  ......
00000024: 00001001 00001000 00001010 00001100 00010100 00001101  ......
0000002a: 00001100 00001011 00001011 00001100 00011001 00010010  ......
00000030: 00010011 00001111 00010100 00011101 00011010 00011111  ......
00000036: 00011110 00011101 00011010 00011100 00011100 00100000  ..... 
xxd: Broken pipe
  • Les bits sont regroupés par octets (8 bits).

  • La première colonne numérote les octets (en hexadécimal).

  • La dernière colonne affiche le caractère ASCII associé à un octet. Seul les octets entre 0010 0000 et 0111 1110 possède un caractère ASCII représentable.

Par défaut, sans l'option '-b', le programme xxd affiche les octets en hexadécimal, ce qui donne une écriture plus concise :

In [ ]:
!xxd mona-lisa.jpeg | head
00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0001  ......JFIF......
00000010: 0001 0000 ffdb 0043 0008 0606 0706 0508  .......C........
00000020: 0707 0709 0908 0a0c 140d 0c0b 0b0c 1912  ................
00000030: 130f 141d 1a1f 1e1d 1a1c 1c20 242e 2720  ........... $.' 
00000040: 222c 231c 1c28 3729 2c30 3134 3434 1f27  ",#..(7),01444.'
00000050: 393d 3832 3c2e 3334 32ff db00 4301 0909  9=82<.342...C...
00000060: 090c 0b0c 180d 0d18 3221 1c21 3232 3232  ........2!.!2222
00000070: 3232 3232 3232 3232 3232 3232 3232 3232  2222222222222222
00000080: 3232 3232 3232 3232 3232 3232 3232 3232  2222222222222222
00000090: 3232 3232 3232 3232 3232 3232 3232 ffc0  22222222222222..
xxd: Broken pipe

Qu'est-ce qu'un fichier de texte

Au niveau physique, une machine informatique ne manipule que des caractères bit 0 et 1. Un disque dur par exemple n'est qu'une succession de bits. Un texte, pour nous être humain, est une suite de caractères au sens de ceux qu'utilisent les langues humaines :

  • Xin chào tất cả mọi người
  • सभी को नमस्कार
  • வணக்கம் அனைவருக்கும்
  • העלא אַלעמען
  • Привет всем
  • 大家好

Tous ces caractères sont désormais répertoriés par la norme Unicode. Chaque caractère est identifié par un nombre appelé son point de code.

Un fichier de texte est un fichier dont le texte binaire s'interprète comme représentant des caractères Unicode. Comme pour n'importe quel type d'information il existe donc un codage qui indique la correspondance entre une suite de bits et un caractère Unicode. Le format d'encodage utilisé par Ubuntu/Linux est le format UTF-8. C'est aussi l'encodage par défaut de l'interpréteur Python.

In [ ]:
"வணக்கம் அனைவருக்கும்".encode('utf-8') 
Out[ ]:
b'\xe0\xae\xb5\xe0\xae\xa3\xe0\xae\x95\xe0\xaf\x8d\xe0\xae\x95\xe0\xae\xae\xe0\xaf\x8d \xe0\xae\x85\xe0\xae\xa9\xe0\xaf\x88\xe0\xae\xb5\xe0\xae\xb0\xe0\xaf\x81\xe0\xae\x95\xe0\xaf\x8d\xe0\xae\x95\xe0\xaf\x81\xe0\xae\xae\xe0\xaf\x8d'
In [ ]:
"Xin chào tất cả mọi người".encode('utf-8')
Out[ ]:
b'Xin ch\xc3\xa0o t\xe1\xba\xa5t c\xe1\xba\xa3 m\xe1\xbb\x8di ng\xc6\xb0\xe1\xbb\x9di'

En Python3, les chaînes de caractères qui sont précédées de la lettre 'b' sont des chaînes de caractères binaires ou plus exactement des chaînes d'octets. Les octets sont représentés par leur écriture en hexadécimal (par exemple '\xc3') ou bien par leur symbole ASCII lorsque celui-ci existe.

In [ ]:
%%writefile essai1.txt
Propriété 1:
∀ x, y ∊ ℝ: x + y = y + x
Overwriting essai1.txt
In [ ]:
!cat essai1.txt
Propriété 1:
∀ x, y ∊ ℝ: x + y = y + x
In [ ]:
!xxd essai1.txt
00000000: 5072 6f70 7269 c3a9 74c3 a920 313a 0ae2  Propri..t.. 1:..
00000010: 8880 2078 2c20 7920 e288 8a20 e284 9d3a  .. x, y ... ...:
00000020: 2078 202b 2079 203d 2079 202b 2078        x + y = y + x
In [ ]:
"∀ x, y ∊ ℝ: x + y = y + x".encode('utf-8')
Out[ ]:
b'\xe2\x88\x80 x, y \xe2\x88\x8a \xe2\x84\x9d: x + y = y + x'

Créer un fichier de texte avec Python

Comme n'importe quel programme exécuté sur Ubuntu/Linux, l'interpréteur Python est automatiquement connecté à un flux de sortie par défaut que l'on appelle la sortie standard. La fonction print a précisément pour effet d'écrire des caractères sur cette sortie standard.

In [ ]:
print("Bonjour à tous\nBienvenu au club")
print("Spam spam spam")
Bonjour à tous
Bienvenu au club
Spam spam spam

Vous pouvez remarquer que la fonction print écrit un caractère saut de ligne après avoir écrit la chaîne de caractères fournie en argument.

In [ ]:
from math import sqrt
x = 2
y = sqrt(x)
print("La racine de %d est environ %.2f" % (x, y))
La racine de 2 est environ 1.41

Pour écrire dans un fichier il suffit de créer un flux de sortie vers ce fichier. Ceci se fait en utilisant la fonction open. La fonction open retourne une valeur qui permet d'utiliser ce flux :

In [ ]:
flux = open("essai3.txt", 'w')

La valeur 'w' indique que le fichier est ouvert en écriture. Un fichier essai3.txt est créé dans le dossier de travail. Pour créer un fichier dans un autre dossier il suffit de préciser un chemin relatif ou absolu.

Si le fichier essai3.txt existe déjà alors son contenu est supprimé. Par contre, si on utilise le caractère 'a' au lieu de 'w' alors le flux de sortie est écrit à la suite du contenu actuel.

In [ ]:
print("Bonjour à tous\nBienvenu au club", file=flux)
print("Spam spam spam", file=flux)
In [ ]:
!cat essai3.txt
In [ ]:
!ls -l essai3.txt
-rw-rw-r-- 1 denis denis 0 sept. 12 15:05 essai3.txt

On constate que les caractères n'ont pas réellement été écrits sur le disque dur. Il est nécessaire de fermer le fichier.

In [ ]:
flux.close()
In [ ]:
!ls -l essai3.txt
-rw-rw-r-- 1 denis denis 48 sept. 12 15:06 essai3.txt
In [ ]:
!cat essai3.txt
Bonjour à tous
Bienvenu au club
Spam spam spam

Lorsqu'un flux est ouvert il doit être fermé dès qu'il n'est plus utilisé par le programme. La fermeture d'un fichier force l'écriture réelle des caractères sur le support physique par le système d'exploitation.

Lire un fichier de texte avec Python

In [ ]:
!head communes.csv
nom, surf_m2, population, nom_dept
Brasles, 7453763.000000000000000, 1.3, AISNE
Étampes-sur-Marne, 2207607.000000000000000, 1.2, AISNE
Brenelle, 4352642.000000000000000, 0.2, AISNE
Belleu, 4537814.000000000000000, 3.9, AISNE
Les Vigneaux, 16155360.000000000000000, 0.5, HAUTES-ALPES
Dhuizel, 6846127.000000000000000, 0.1, AISNE
Verdilly, 5092646.000000000000000, 0.4, AISNE
Gland, 5618063.000000000000000, 0.5, AISNE
Cuffies, 5047339.000000000000000, 1.6, AISNE

Pour lire le contenu d'un fichier de texte il faut tout d'abord créer un flux d'entrée en utilisant la fonction open avec le caractère r.

In [ ]:
flux = open("communes.csv", "r")

Il est possible de lire le fichier dans son intégralité en utilisant la méthode readlines :

In [ ]:
lines = flux.readlines()
In [ ]:
len(lines)
Out[ ]:
36726
In [ ]:
lines[:10]
Out[ ]:
['nom, surf_m2, population, nom_dept\n',
 'Brasles, 7453763.000000000000000, 1.3, AISNE\n',
 'Étampes-sur-Marne, 2207607.000000000000000, 1.2, AISNE\n',
 'Brenelle, 4352642.000000000000000, 0.2, AISNE\n',
 'Belleu, 4537814.000000000000000, 3.9, AISNE\n',
 'Les Vigneaux, 16155360.000000000000000, 0.5, HAUTES-ALPES\n',
 'Dhuizel, 6846127.000000000000000, 0.1, AISNE\n',
 'Verdilly, 5092646.000000000000000, 0.4, AISNE\n',
 'Gland, 5618063.000000000000000, 0.5, AISNE\n',
 'Cuffies, 5047339.000000000000000, 1.6, AISNE\n']

Cependant, ce n'est généralement pas une bonne méthode. Si le fichier est très gros on risque en effet d'encombrer inutilement la mémoire RAM. Cela est souvent inutile car ne traite les lignes que les unes après les autres.

On privilège donc la méthode readline (au singulier).

In [ ]:
flux = open("communes.csv", "r")
In [ ]:
flux.readline()
Out[ ]:
'nom, surf_m2, population, nom_dept\n'
In [ ]:
flux.readline()
Out[ ]:
'Brasles, 7453763.000000000000000, 1.3, AISNE\n'
In [ ]:
flux.readline()
Out[ ]:
'Étampes-sur-Marne, 2207607.000000000000000, 1.2, AISNE\n'

À chaque appel, l'instruction flux.readline() lit la suite du flux de caractères jusqu'au prochain caractère saut de ligne et retourne ces caractères dans une valeur de type str.

La structure du code est alors la suivante :

  • Ouverture du flux le plus tard possible
  • Lecture et traitement de chaque ligne dans une boucle
  • Fermeture du fichier le plus tôt possible
In [ ]:
cpt = 0
flux = open("communes.csv", "r")
line = flux.readline()
while line != '':
    cpt = cpt +  len(line)
    line = flux.readline()
flux.close()
print(cpt)
1978818

La construction for in permet d'écrire un code plus élégant en masquant les appels à readline. Ces appels sont faits implicitement :

In [ ]:
cpt = 0
flux = open("communes.csv", "r")
for line in flux:
    cpt = cpt +  len(line)
flux.close()
print(cpt)
1978818

Ou de façon plus Pythonesque :

In [ ]:
flux = open("communes.csv", "r")
cpt = sum(len(line) for line in flux)
flux.close()
print(cpt)
1978818