Aller au contenu

Fichier CSV et SGBD

Voici 2 activités assez différentes, mais complémentaires :

  1. Une sur la manipulation de données sur des fichiers CSV (ce qui permet de réviser au passage le traitement de données en table). On met en évidence les difficultés à s'assurer
    • d'une part la bonne validité d'une condition simple : ne pas avoir 2 pseudos identiques dans une table,
    • d'autre part la bonne manipulation des données par diverses instances du programme d'inscription.
  2. La deuxième permet d'appréhender un SGBD avec l'utilisation du logiciel DB Browser.

Manipulations de données sur des fichiers CSV

Révisions sur le traitement en table avec Python

Le format CSV

Le format CSV (Comma Separated Values, données avec des séparateurs) est un format de fichier universel et le plus simple à manipuler, notamment en Python.

C'est aussi un format très flexible : on peut troquer la virgule contre un point virgule ou une tabulation comme séparateur de données, si celles-ci contiennent elles-mêmes des virgules (par exemple en français où la virgule peut être utilisée pour écrire des nombres décimaux).

matable.csv
Nom;Prénom;Âge;Pseudo
Garcia;Serge;40;sergent_garcia
De la Vega;Diego;17;ZoRRo
McDuck;Scrooge;108;Picsou

Il faut imaginer le contenu de ce fichier comme un tableau, dont les lignes sont les lignes du fichier, et les séparations entre les colonnes sont indiquées par les caractères ;.

La première ligne contient des éléments particuliers appelés descripteurs, qui indiquent quelle donnée contient la colonne correspondante.

Ici, il s'agit de Nom, Prénom, Âge et Pseudo.

Chacune des lignes suivantes contient un enregistrement, qui associe une valeur à chaque descripteur. Les valeurs sont données dans le même ordre que le sont les descripteurs dans la première ligne.

Ici, par exemple, la dernière ligne contient l'enregistrement :

\[ \begin{cases} Nom=McDuck\\ Prénom=Scrooge\\ Âge=108\\ Pseudo=Picsou \end{cases} \]

Ouverture d'une table CSV en Python

Il existe 2 façons pour ouvrir un fichier en Python :

fichier_source = open('matable.csv','r',encoding='utf-8')

et

with open('matable.csv','r',encoding='utf-8') as fichier_source:

Ces 2 façons ouvrent toutes les deux un fichier matable.csv en lecture seule ('r') avec un encodage en UTF-8.

La ligne de code fichier_source = open('matable.csv','r',encoding='utf-8') affecte à fichier_source un objet python fournissant une interface _io.TextIOWrapper permettant d'effectuer diverses opérations sur le fichier ouvert, dont obtenir son contenu.

La ligne with open('matable.csv','r',encoding='utf-8') as fichier_source: effectue la même affectation, mais limite la validité de l'objet fichier_source au bloc indenté immédiatement en dessous. Au-delà de ce bloc, le fichier n'est plus ouvert et y accéder via fichier_source provoque une erreur d'exécution.

Lorsque l'on n'utilise pas de bloc with, il convient de fermer le fichier avec la méthode close() de l'objet fichier_source. Si on omet cette opération, python se charge de fermer lui-même le fichier, mais seulement lorsqu'il est certain que l'objet fichier_source n'est plus utilisé, ce qui peut être à la fin du programme.

Ces 2 codes sont donc équivalents :

# Premier code 
fichier_source = open('matable.csv','r',encoding='utf-8')
# des traitements...
fichier_source.close()

# Second code
with open('matable.csv','r',encoding='utf-8') as fichier_source:
     pass # des traitements...

Lecture d'une table CSV en python

On pourra utiliser la méthode Python readlines()

with open('matable.csv','r',encoding='utf-8') as fichier_source:
    mon_objet = fichier_source.readlines()

La méthode readlines() de l'objet fichier_source affecte à mon_objet un tableau de chaînes de caractères correspondant, dans l'ordre de lecture, aux différentes lignes du fichier texte ouvert dans mon_objet.

Après exécution, on aurait ici dans une console python :

>>> mon_objet
['Nom;Prénom;Âge;Pseudo\n', 'Garcia;Serge;40;sergent_garcia\n', 'De la Vega;Diego;17;ZoRRo\n', 'McDuck;Scrooge;108;Picsou\n']

À noter la présence du caractère de saut de ligne \n, qui indique le changement de ligne tout en faisant partie de la ligne.

Chaque enregistrement du fichier CSV donné en exemple est un quadruplet nommé représenté en python par un dictionnaire proposant quatre clefs. Le dernier enregistrement est donc le suivant :

{'Nom': 'McDuck', 'Prénom': 'Scrooge', 'Âge': '108', 'Pseudo': 'Picsou'}

Obtenir la liste des clés de l'objet sans ; et \n

La première ligne du fichier est le premier élément du tableau mon_objet.

premiere_ligne = mon_objet[0]
print(premiere_ligne)

Cette première ligne contient le saut de ligne '\n' que l'on voit en imprimant quelques caractères dessous. On peut utiliser la méthode rstrip() pour le supprimer.

premiere_ligne = mon_objet[0]
print(premiere_ligne)
print('--')
chaine_des_descr = premiere_ligne.rstrip()
print(chaine_des_descr)
print('--')

Le résultat obtenu ressemble à cela :

Nom;Prénom;Âge;Pseudo

... que l'on peut découper pour obtenir une liste avec la méthode split() :

liste_clefs = chaine_des_descr.split(';')

ou en combinant les méthodes :

liste_clefs = mon_objet[0].rstrip().split(';')

Ainsi, on obtient :

['Nom', 'Prénom', 'Âge', 'Pseudo']

Manipulation de données au format .csv

Manipuler un nouveau fichier CSV

  1. Créer un fichier table_films.csv à partir de la table film contenant qautre enregistrements et cinq descripteurs (nom, pays, année, genre et réalisateur).
  2. Créer le programme Python lecture_csv.py de lecture de cette table permettant de lister l'objet Python complet et le 3è enregistrement,
  3. Afficher enfin dans une liste les cinq descripteurs de la table.

Il s'agit finalement de refaire la partie de révisions appliquée à cette nouvelle table.

Fonction importer_csv()

À partir de maintenant, on reprend le fichier matable.csv de la section précédente (celui avec les pseudos).

On vous propose la fonction importer_csv() suivante :

def importer_csv(fichier_ouvert):
    """
    ...
    """
    # On récupère les lignes du fichier
    lignes=fichier_ouvert.readlines()

    clefs=lignes[0].rstrip().split(';')

    enregistrements=[] # On initialise la liste des enregistrements obtenus
    for i in range(1,len(lignes)): # On parcourt...
        valeurs=lignes[i].rstrip().split(';') # On extrait les valeurs
        enregistrements.append({}) # On prépare le dctionnaire qui recueille les valeurs
        for j in range(len(clefs)): # On parcourt les descripteur
            enregistrements[i-1][clefs[j]]=valeurs[j] # On associe à chaque clef sa valeur
    return clefs,enregistrements

Bien comprendre cette fonction et compléter le docstring de cette fonction, mais aussi les commentaires.

Détection d'un potentiel doublon

On se propose de s'inspirer de la fonction précédente pour créer une fonction pseudo_present() qui prend en entrée un fichier (matable.csv via l'objet fichier_ouvert) et une chaîne de caractère pseudo, qui sera notre pseudo à tester.

Si le pseudo est présent dans la table, on renvoie True, sinon False.

Compléter la fonction suivante (les ...) afin de réaliser cette opération :

def pseudo_present(fichier_ouvert,pseudo):
    """
    Vérifie la présence d'un pseudo dans une table de comptes
    """
    # On récupère les lignes
    ...

    ...: # On parcourt les lignes du fichier à partir de la deuxième (la première ce sont les descripteurs)
        valeurs=... # On extrait les valeurs
        if ...:
            return True # On renvoie True si on trouve le pseudo
    return False # Sinon, on n'a pas trouvé le pseudo

Lire une documentation

Nous allons utiliser une nouvelle méthode pour manipuler notre fichier :

with open('matable.csv','a',encoding='utf-8') as utilisateurs:
    pass

À l'aide du terminal de Pyzo (ou d'un terminal Python), appeler l'aide de la méthode open. On pourra utiliser pour cela help().

Donner la signification du paramètre 'a'.

Insérer un enregistrement

Compléter le code de la fonction inserer() ci-dessous. On n'utilisera que la fonction print pour écrire dans le fichier.

Auparavant, vous aurez fait appel à l'aide de la fonction print() afin de prendre en compte le paramètre sep, mais aussi file ; l'idée était bien sûr d'écrire (print()) dans le fichier_ouvert...

def inserer(fichier_ouvert, enregistrement):
    """
    Insère un enregistrement à la fin d'un fichier csv
    Paramètres :
      fichier_ouvert : ficher csv ouvert en écriture, pointeur à la fin
      enregistrement : dictionnaire représentant l'objet à enregistrer dans la table
    Valeur renvoyée : None
    """
    pass

Limites du CSV

Nous allons voir dans ces deux sous-parties, les limites de la manipulation de fichier CSV avec un langage de programmation tel que Python.

Un problème de validité des données

Créer un fichier utilisateurs.csv avec les descripteurs précédents : Nom, Prénom, Âge et Pseudo

Créer un module inscription.py en complétant le code ci-dessous, pour cela :

  • Compléter le module, sous la ligne if __name__=='__main__': dans le squelette ci-dessous, afin qu'à l'exécution, ce module
    • demande un pseudo,
    • vérifie qu'il n'est pas dans la table utilisateurs.csv, affiche Pseudo déjà utilisé et termine s'il y est,
    • demande le nom, le prénom et l'âge,
    • inscrit l'utilisateur dans la table utilisateurs.csv.
def pseudo_present(table, pseudo):
    """
    Vérifie la présence d'un pseudo dans une table de comptes
    """
    pass

def inserer(nom_fichier, clefs, enregistrement):
    """
    Insère un enregistrement à la fin d'un fichier csv
    Paramètres :
      nom_fichier : chemin d'un fichier csv (chaine de caractères)
      clefs : tableau des descripteurs du fichier csv
      enregistrement : dictionnaire dont les clefs sont les éléments de clefs, auxquelle sont associées des chaines de caractères
    Valeur renvoyée : None
    """
    pass

if __name__=='__main__':
    pseudo = input("Entrez votre pseudo : ")

    # On importe la table utilisateur et on teste si le pseudo existe
    ...

    if ...: # On vérifie la présence du pseudo
        print("Pseudo déjà utilisé, désolé")
    else: # On continue l'insctiption si le pseudo est disponible
        nom = input("Entrez votre nom : ")
        prénom = input("Entrez votre prénom : ")
        age = input("Entrez votre âge : ")

        # On crée le dictionnaire avec les informations
        ...
        # On effectue l'inscription dans la table avec inserer()
        ...

        print("Bienvenue.")
Simulations d'inscriptions parallèles

Imaginer les problèmes que peut poser ce système s’il est utilisé pour plusieurs inscriptions simultanées, par exemple sur un serveur.

  • Ouvrir deux shells Python dans Pyzo.
  • Dans le premier, lancer le programme puis saisir un pseudo, par exemple 'Roger'.
  • Passer au deuxième shell, lancer le programme, saisir le même pseudo puis terminer l'inscription
  • Terminer l'inscription dans le premier terminal.

Observer le contenu de utilisateurs.csv ; que constatez-vous ? Expliquez...

Simulations d'inscriptions parallèles semi-automatisés

Créer un module simulation.py qui a le même fonctionnement, mais non interactif : il remplace les entrées de l'utilisateur par le choix aléatoire d'une chaîne de caractères dans un tableau de cinq possibilités

On pourra utiliser la fonction choice() du module random pour le choix aléatoire dans une liste.

Ajouter un temps d'attente aléatoire après la vérification de la présence du pseudo (pour simuler une recherche plus longue dans une très grosse table).

Pour ce temps aléatoire, on peut donner en paramètre de la fonction sleep() du module time le nombre 0.5*random(), où random() est importée du module random, pour une attente d'une durée aléatoire entre 0 et 500ms.

Utiliser une commande ou un script shell pour lancer en parallèle dix instances de ce programme. Constater encore une fois que l'unicité du pseudo n'est pas garantie. Sous Linux, on rappelle que :

  • La commande python3 simulation.py lance le programme et attend la fin de son exécution.
  • La commande python3 simulation.py & lance le programme et rend immédiatement la main, ce qui permet de faire autre chose en parallèle de l'exécution du module python, si elle est encore en cours (uniquement sur Linux).

On peut alors utiliser la syntaxe suivante :

start.sh
for i in {1..10}
do
  MaCommande &
done
start.bat
for /L %%i in (1,1,10) do (
    start /b MaCommande
)

Attention ! Sur Windows, on veillera à indiquer les liens complets sous la forme : U:\\eleves\\..., dans le fichier Python et dans la commande bat.

Un problème d'intégrité des données

On propose une parade simple au problème qui se pose dans la partie précédente : garder le fichier ouvert entre la recherche du pseudo et l'inscription finale, pour bloquer l'accès à la table entre ces deux étapes et ainsi garantir la validité de la vérification.

Obtenir la liste des modes d'ouverture utilisables en deuxième paramètre de open() et choisir le plus adapté à notre nouveau besoin.

On évite les modes 'x', 'w' et 'a' qui ne conviennent pas. Dans notre cas, on choisit 'r+'. Explicitez ce mode...

Effectuer une copie du module simulation.py qui s'appelle simulation2.py. Le modifier pour qu'il n'effectue qu'une seule ouverture du fichier, en mode 'r+', au début du bloc if __name__=='__main__':, puis le ferme à la fin de ce bloc avec .close().

Ajouter également, avec print(), des indications au début et à la fin de ce bloc, pour donner des indications supplémentaires du bon traitement des données.

Exécuter 10 fois ce script en parallèle, comme précédemment. Qu'observe-t-on ?

Conclusion et une solution : les SGBD

Remarque

Dans cette activité, nous avons repéré de nombreux problèmes avec la manipulation des fichiers CSV. Aussi, nous vous proposons un SGBD pour répondre à ces besoins et assurer l'intégrité et la validité des données.

SGBD

Un Système de Gestion de Bases de Données permet de garantir l'intégrité des données, par exemple en prenant la forme d'un serveur qui centralise tous les accès à la base de données, et aussi leur validité, en traitant les demandes de consultation ou d'écriture sous la forme de requêtes qui effectuent des opérations de vérifications dictées par le schéma relationnel de la base de données.

Systèmes de Gestion de Bases de Données (SGBD)

Principes des SGBD

Après avoir consulté la vidéo, répondre aux questions ci-dessous :

  1. Pourquoi ne peut-on pas utiliser de fichiers CSV pour gérer une base de données ?
  2. Pourquoi utilise-t-on plusieurs serveurs pour stocker les mêmes données ?
  3. Qu'est-ce qu'un accès concurrent ?
  4. Quelle est la différence entre une base de données et un SGBD ?
  5. Quels services permet donc un SGBD ?
  6. Quel est l'intérêt d'utiliser un SGBD-R ? Quel langage utilise un SGBD-R ?

Prise en mais d'un SGBDR

Vous allez utiliser un logiciel permettant la mise en œuvre d'un SGBDR de façon simplifiée. Afin de le découvrir, suivez les étapes ci-dessous, et explorez de vous-même le logiciel. Vous devriez voir, lors de vos manipulations, une syntaxe ressemblant à un langage de programmation. Il s'agit du SQL ! Jetez-y un œil pour voir à quoi cela ressemble. Ce sera bientôt à vous d'écrire ce texte, et non à une interface graphique !

En utilisant le logiciel DB Browser, construire la base de données présente dans la capsule vidéo et dont le schéma relationnel est rappelé ci-dessous.

Préciser le type de chaque attribut de cette table.

Schéma Relationnel base film

Compléter ensuite la base avec les données de films et de réalisateur.

Rappel de la base film

Table film

Conformément au schéma relationnel suivant, adapter votre base de données en créant deux nouvelles relations :

  • Relation nationalité ou pays
  • Relation genre

Schéma relationnel complet

Alimenter enfin correctement cette base.