<jupyter_start><jupyter_text>1A.2 - Modules, fichiers, expressions régulièresLe langage Python est défini par un ensemble de règle, une grammaire. Seul, il n'est bon qu'à faire des calculs. Les modules sont des collections de fonctionnalités pour interagir avec des capteurs ou des écrans ou pour faire des calculs plus rapides ou plus complexes.<jupyter_code>from jyquickhelper import add_notebook_menu
add_notebook_menu()<jupyter_output><empty_output><jupyter_text>FichiersLes fichiers permettent deux usages principaux :- récupérer des données d'une exécution du programme à l'autre (lorsque le programme s'arrête, toutes les variables sont perdues)- échanger des informations avec d'autres programmes (Excel par exemple).Le format le plus souvent utilisé est le fichier plat, texte, txt, csv, tsv. C'est un fichier qui contient une information structurée sous forme de matrice, en ligne et colonne car c'est comme que les informations numériques se présentent le plus souvent. Un fichier est une longue séquence de caractères. Il a fallu choisir une convention pour dire que deux ensembles de caractères ne font pas partie de la même colonne ou de la même ligne. La convention la plus répandue est :* ``\t`` : séparateur de colonnes* ``\n`` : séparateur de lignesLe caractère ``\`` indique au langage python que le caractère qui suit fait partie d'un code. Vous trouverez la liste des codes : [String and Bytes literals](https://docs.python.org/3.4/reference/lexical_analysis.htmlstring-and-bytes-literals).**Aparté :** aujourd'hui, lire et écrire des fichiers est tellement fréquent qu'il existe des outils qui font ça dans une grande variété de formats. Vous découvrirez cela lors de la [séance 10](http://www.xavierdupre.fr/app/ensae_teaching_cs/helpsphinx/notebooks/td1a_cenonce_session_10.html). Il est utile pourtant de le faire au moins une fois soi-même pour comprendre la logique des outils et pour ne pas être bloqué dans les cas non prévus.Ecrire et lire des fichiers est beaucoup plus long que de jouer avec des variables. Ecrire signifie qu'on enregistre les données sur le disque dur : elles passent du programme au disque dur (elles deviennent permanentes). Elles font le chemin inverse lors de la lecture.  EcritureIl est important de retenir qu'un fichier texte ne peut recevoir que des chaînes de caractères.<jupyter_code>mat = [[1.0, 0.0],[0.0,1.0] ]                    # matrice de type liste de listes
with open ("mat.txt", "w") as f :                # création d'un fichier en mode écriture
    for i in range (0,len (mat)) :               # 
        for j in range (0, len (mat [i])) :      # 
            s = str (mat [i][j])                 # conversion en chaîne de caractères
            f.write (s + "\t")                   #
        f.write ("\n")                           # 
        
# on vérifie que le fichier existe : 
import os
print([ _ for _ in os.listdir(".") if "mat" in _ ] )

# la ligne précédente utilise le symbole _ : c'est une variable 
# le caractère _ est une lettre comme une autre
# on pourrait écrire :
# print([ fichier for fichier in os.listdir(".") if "mat" in fichier ] )
# on utilise cette convention pour dire que cette variable n'a pas vocation à rester<jupyter_output>['mat.txt']<jupyter_text>Le même programme mais écrit avec une écriture consendée :<jupyter_code>mat = [[1.0, 0.0],[0.0,1.0] ]             # matrice de type liste de listes
with open ("mat.txt", "w") as f :        # création d'un fichier
    s = '\n'.join ( '\t'.join( str(x) for x in row  ) for row in mat )
    f.write ( s )
    
# on vérifie que le fichier existe : 
print([ _ for _ in os.listdir(".") if "mat" in _ ] )<jupyter_output>['mat.txt', 'mat2.txt']<jupyter_text>On regare les premières lignes du fichier ``mat2.txt`` :<jupyter_code>import pyensae
%load_ext pyensae
%head mat.txt<jupyter_output><empty_output><jupyter_text>Lecture<jupyter_code>with open ("mat.txt", "r") as f : # ouverture d'un fichier
    mat = [ row.strip(' \n').split('\t') for row in f.readlines() ]
print(mat)<jupyter_output>[['1.0', '0.0'], ['0.0', '1.0']]<jupyter_text>On retrouve les mêmes informations à ceci près qu'il ne faut pas oublier de convertir les nombres initiaux en ``float``.<jupyter_code>with open ("mat.txt", "r") as f :                # ouverture d'un fichier
    mat = [ [ float(x) for x in row.strip(' \n').split('\t') ] for row in f.readlines() ]
print(mat)<jupyter_output>[[1.0, 0.0], [0.0, 1.0]]<jupyter_text>Voilà qui est mieux. Le module [os.path](https://docs.python.org/3.4/library/os.path.html) propose différentes fonctions pour manipuler les noms de fichiers. Le module [os](http://docs.python.org/3.3/library/os.htmlmodule-os) propose différentes fonctions pour manipuler les fichiers :<jupyter_code>import os
for f in os.listdir('.'):
    print (f)<jupyter_output>.ipynb_checkpoints
etatcivil2012_mar2012_dbase.zip
etatcivil2012_nais2012_dbase.zip
example_bokeh.html
exemple.xlsx
facebook
facebook.tar.gz
graph_notebook_ski.png
lisezmoi_MAR2012.txt
lisezmoi_NAIS2012.txt
mar2012.dbf
mat.txt
mat2.txt
nais2012.dbf
patates.png
pngbokeh.png
python_filtre.png
python_filtre2.png
python_filtre3.png
td10exc.png
td11_cor_grid.png
td1a_cenonce_session1.ipynb
td1a_cenonce_session2.ipynb
td1a_cenonce_session3.ipynb
td1a_cenonce_session4.ipynb
td1a_cenonce_session5.ipynb
td1a_cenonce_session6.ipynb
td1a_cenonce_session7.ipynb
td1a_cenonce_session8.ipynb
td1a_cenonce_session9.ipynb
td1a_cenonce_session_10.ipynb
td1a_cenonce_session_11.ipynb
td1a_cenonce_session_12.ipynb
td1a_correction_session1.ipynb
td1a_correction_session2.ipynb
td1a_correction_session3.ipynb
td1a_correction_session4.ipynb
td1a_correction_session5.ipynb
td1a_correction_session6.ipynb
td1a_correction_session7.ipynb
td1a_correction_session8.ipynb
td1a_correction_session9.ipynb
td1a_correction_ses[...]<jupyter_text>withDe façon pragmatique, l'instruction [with](https://docs.python.org/3.4/reference/compound_stmts.htmlwith) permet d'écrire un code plus court d'une instruction : ``close``. Les deux bouts de code suivant sont équivalents :<jupyter_code>with open("exemple_fichier.txt", "w") as f:
    f.write("something")
f = open("exemple_fichier.txt", "w")
f.write("something")
f.close()<jupyter_output><empty_output><jupyter_text>L'instruction ``close`` *ferme* le fichier. A l'ouverture, le fichier est réservé par le programme Python, aucune autre application ne peut écrire dans le même fichier. Après l'instruction ``close``, une autre application pour le supprimer, le modifier. Avec le mot clé ``with``, la méthode ``close`` est implicitement appelée.  à quoi ça sert ?On écrit très rarement un fichier texte. Ce format est le seul reconnu par toutes les applications. Tous les logiciels, tous les langages proposent des fonctionnalités qui exportent les données dans un format texte. Dans certaines circonstances, les outils standards ne fonctionnent pas - trop grops volumes de données, problème d'encoding, caractère inattendu -. Il faut se débrouiller.  Exercice 1 : Excel $\rightarrow$ Python $\rightarrow$ Excel    Il faut télécharger le fichier [seance4_excel.xlsx](http://www.xavierdupre.fr/enseignement/complements/seance4_excel.xlsx) qui contient une table de trois colonnes. Il faut :- enregistrer le fichier au format texte,- le lire sous python- créer une matrice carrée 3x3 où chaque valeur est dans sa case (X,Y),- enregistrer le résultat sous format texte,- le récupérer sous Excel.      Autres formats de fichiersLes fichiers texte sont les plus simples à manipuler mais il existe d'autres formats classiques~:- [html](http://fr.wikipedia.org/wiki/Hypertext_Markup_Language) : les pages web- [xml](http://fr.wikipedia.org/wiki/Extensible_Markup_Language) : données structurées- [zip](http://fr.wikipedia.org/wiki/ZIP_(format_de_fichier), [gz](http://fr.wikipedia.org/wiki/Gzip) : données compressées- [wav](http://fr.wikipedia.org/wiki/WAVEform_audio_format), [mp3](http://fr.wikipedia.org/wiki/MPEG-1/2_Audio_Layer_3), [ogg](http://fr.wikipedia.org/wiki/Ogg) : musique- [mp4](http://fr.wikipedia.org/wiki/MPEG-4), [Vorbis](http://fr.wikipedia.org/wiki/Vorbis) : vidéo- ...  ModulesLes modules sont des extensions du langages. Python ne sait quasiment rien faire seul mais il bénéficie de nombreuses extensions. On distingue souvent les extensions présentes lors de l'installation du langage (le module [math](https://docs.python.org/3.4/library/math.html)) des extensions externes qu'il faut soi-même installer ([numpy](http://www.numpy.org/)). Deux liens :- [modules officiels](http://docs.python.org/3.4/library/)- [modules externes](https://pypi.python.org/pypi)Le premier réflexe est toujours de regarder si un module ne pourrait pas vous être utile avant de commencer à programmer. Pour utiliser une fonction d'un module, on utilise l'une des syntaxes suivantes :<jupyter_code>import math
print (math.cos(1))

from math import cos
print (cos(1))

from math import *    # cette syntaxe est déconseillée car il est possible qu'une fonction
print (cos(1))        # porte le même nom qu'une des vôtres<jupyter_output>0.5403023058681397
0.5403023058681397
0.5403023058681397<jupyter_text>Exercice 2 : trouver un module (1)Aller à la page [modules officiels](http://docs.python.org/3.4/library/) (ou utiliser un moteur de recherche) pour trouver un module permettant de générer des nombres aléatoires. Créer une liste de nombres aléatoires selon une loi uniforme puis faire une permutation aléatoire de cette séquence.  Exercice 3 : trouver un module (2)Trouver un module qui vous permette de calculer la différence entre deux dates puis déterminer le jour de la semaine où vous êtes nés.  Module qu'on crée soi-mêmeIl est possible de répartir son programme en plusieurs fichiers. Par exemple, un premier fichier ``monmodule.py`` qui contient une fonction :<jupyter_code># fichier monmodule.py
import math

def fonction_cos_sequence(seq) :
    return [ math.cos(x) for x in seq ]

if __name__ == "__main__" :
    # et une petite astuce quand on travaille sous notebook
    code = """
        # -*- coding: utf-8 -*-
        import math
        def fonction_cos_sequence(seq) :
            return [ math.cos(x) for x in seq ]        
        if __name__ == "__main__" :
            print ("ce message n'apparaît que si ce programme est le point d'entrée")
        """.replace("        ","")
    with open("monmodule.py", "w", encoding="utf8") as f :
        f.write(code)<jupyter_output><empty_output><jupyter_text>Le second fichier :<jupyter_code>import monmodule

print ( monmodule.fonction_cos_sequence ( [ 1, 2, 3 ] ) )<jupyter_output>[0.5403023058681397, -0.4161468365471424, -0.9899924966004454]<jupyter_text>Exercice 4 : son propre moduleQue se passe-t-il si vous remplacez ``if __name__ == "__main__":``  par ``if True :`` ?  Expressions régulièresPour la suite de la séance, on utilise comme préambule les instructions suivantes :<jupyter_code>import pyensae
discours = pyensae.download_data('voeux.zip', website = 'xd')<jupyter_output>downloading of  http://www.xavierdupre.fr/enseignement/complements/voeux.zip  to  voeux.zip
    unzipped  VOEUX01.txt  to  .\VOEUX01.txt
    unzipped  VOEUX05.txt  to  .\VOEUX05.txt
    unzipped  VOEUX06.txt  to  .\VOEUX06.txt
    unzipped  VOEUX07.txt  to  .\VOEUX07.txt
    unzipped  VOEUX08.txt  to  .\VOEUX08.txt
    unzipped  VOEUX09.txt  to  .\VOEUX09.txt
    unzipped  VOEUX74.txt  to  .\VOEUX74.txt
    unzipped  VOEUX75.txt  to  .\VOEUX75.txt
    unzipped  VOEUX79.txt  to  .\VOEUX79.txt
    unzipped  VOEUX83.txt  to  .\VOEUX83.txt
    unzipped  VOEUX87.txt  to  .\VOEUX87.txt
    unzipped  VOEUX89.txt  to  .\VOEUX89.txt
    unzipped  VOEUX90.txt  to  .\VOEUX90.txt
    unzipped  VOEUX94.txt  to  .\VOEUX94.txt<jupyter_text>La documentation pour les expressions régulières est ici : [regular expressions](http://docs.python.org/3.3/library/re.html). Elles permettent de rechercher des motifs dans un texte :- _4 chiffres / 2 chiffres / 2 chiffres_ correspond au motif des dates, avec une expression régulière, il s'écrit : ``[0-9]{4}/[0-9]{2}/[0-9]{2}``- _la lettre a répété entre 2 et 10 fois_ est un autre motif, il s'écrit : ``a{2,10}``.<jupyter_code>import re   # les expressions régulières sont accessibles via le module re
expression = re.compile("[0-9]{2}/[0-9]{2}/[0-9]{4}")
texte = """Je suis né le 28/12/1903 et je suis mort le 08/02/1957. Ma seconde femme est morte le 10/11/63."""
cherche = expression.findall(texte)
print(cherche)<jupyter_output>['28/12/1903', '08/02/1957']
{'fraction_non_alphanumeric': 0.10108939442486382, 'fraction_numerical': 0.03132008971483499, 'mean_word_length': 4.252419015565839, 'pattern_counts': {'":': 1, '<': 44, '<?xml version=': 0, '>': 44, 'https://': 5, 'lorem ipsum': 0, 'www.': 4, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '522604', 'license': 'MIT', 'n_tokens_mistral': 4627, 'n_tokens_neox': 4440, 'n_words': 1321, 'path': '_doc/notebooks/td1a/td1a_cenonce_session4.ipynb', 'repo_name': 'mohamedelkansouli/Ensae_py', 'size': '12484'}