La mémoire secondaire n’est pas directement accessible (les programmes n’ont pas la possibilité d’aller écrire directement sur le disque). Le système d’exploitation assure ainsi l’indépendance du programme par rapport à l’emplacement des données, au travers d’instructions d’entrée/sortie spécialisées.
Pour que les programmes puissent écrire sur le disque, on introduit des objets intermédiaires : les fichiers. Un fichier est caractérisé par (nom, emplacement (volume, arborescence), droit d’accès, taille,…). Il s’agit d’une entité logique. Tout programme utilisant un fichier passe par le système d’exploitation qui, à partir des informations, détermine la localisation des données sur le support.
Organisation hiérarchique :
La consultation des données nécessite d’ouvrir une “communication” entre le programme et les fichiers. Ce canal de communication permet de recevoir un flux de données.
Pour établir la communication, il faut connaître : le “chemin d’accès” aux données (disque dur local) l’”adresse” des données (lorsqu’il s’agit de données stockées sur un serveur distant)
L’opération d’ouverture de fichier initialise un descripteur de fichier, qui sert à désigner (dans le programme) le fichier sur lequel on travaille, et d’accéder au contenu du flux.
FileReader f = new FileReader("/chemin/vers/mon/fichier.txt");
Les opérateurs de lecture et d'écriture ne travaillent pas directement sur les données du fichier. Les données du fichier sont dans une mémoire “tampon”. Lors des opération de lecture, la mémoire tampon est mise à jour au fur et à mesure qu’on avance dans la lecture du fichier par des opérations de lecture sur le disque.
BufferedReader br = new BufferedReader(f);
On utilise la gestion des exceptions permettant de prévoir une action de secours lorsqu’une une opération “risquée” échoue.
Lorsque l’opération d’ouverture est réalisée avec succès, le flux de données devient accessible en lecture (les premières lignes du fichier sont chargées en mémoire et une tête de lecture se positionne sur le premier caractère de la première ligne). Il ne reste plus qu’à lire les données.
La consultation des données s’effectue séquentiellement à l’aide de l’opérateur de lecture readLine
. Chaque appel à cet opérateur charge les nouvelles données et déplace la tête de lecture vers les données suivantes. Cette opération peut être effectuée plusieurs fois jusqu’à atteindre la fin de fichier.
try (BufferedReader br = new BufferedReader(new FileReader("/chemin/vers/mon/fichier.txt"))) { String ligne; while ((ligne = br.readLine()) != null) { System.out.println(ligne); } } catch (IOException e) { e.printStackTrace(); }
Voir aussi :
Tout commence par une fiche à remplir…
Un formulaire se présente sous la forme d’un ensemble de rubriques à remplir.
On peut donc générer à partir d'un modèle de formulaire un grand nombre de données obéissant au même format*
Exemple: un schéma de données décrivant des informations sur des clients avec des colonnes pour le nom, l'adresse, et le numéro de téléphone
Ces données sont généralement stockées dans des formats tabulaires (comme les tableaux ou fichiers CSV) où chaque donnée est placée dans des champs ou colonnes clairement définis.
Un tableau de données est une liste (finie et ordonnée) de tuples, chaque tuple obéissant à un même schéma R.
Ce type de données est facilement lisible par les machines et exploitable grâce à des langages de requête.
Les principaux formats d'échange de données sont :
Chaque enregistrement est codé sur une ligne, chaque valeur étant séparé par un caractère séparateur (virgule, point-virgule, tabulation,…).
Exemple :
Dubois,Martine,"28, rue des Lilas, 45000 Orléans",45
Remarques :
Exemples :
Exemple :
{"nom" : "Dubois", "prénom" : "Martine", "adresse" : "28, rue des Lilas, 45000, Orléans", "âge" : 45}
Remarques :
Pour les données organisées de manière hiérarchique. Des balises servent à séparer les différents attributs.
Ex :
<nom> Dubois </nom> <prénom> Martine </prénom> <adresse> <num> 28 </num> <voie> rue des Lilas </voie> <code postal> 45000 </code postal> <ville> Orléans </ville> </adresse> <âge> 45 </âge>
remarque : le format json
permet également de définir des hiérarchies
{ "nom" : "Dubois", "prénom" : "Martine", "adresse" : { "numero" : 28, "voie" : "rue des Lilas", "code_postal" : 45000, "ville" : "Orléans" }, "âge" : 45 }
Exemples :
Le Tuple est la structure de données de base servant pour le recueil, le transport et le stockage des données.
t=(a1,...,am) Exemple :
("Dubois", "Martine", 22, "29/10/1994", "Orléans")
Pour une gestion efficace des données, il est nécessaire de pouvoir identifier chaque jeu de valeurs (tuple) de façon unique.
L'indexation des données repose sur un principe simple d'étiquetage consistant à attribuer une étiquette différente à chaque enregistrement.
On peut représenter l'opération d'indexation sous la forme d'une fonction. Si d est le jeu de valeurs, k(d) désigne l'identifiant de ce jeu de valeurs.
L'indexation suppose l'existence d'une bijection entre l'ensemble des données et l'ensemble des clés, permettant d'assurer l'unité et la spécificité de la clé
L'identifiant doit en pratique tenir sur un nombre d'octets le plus petit possible pour que la liste L puisse être manipulée en mémoire centrale. Autrement dit, il faut que :
pour que :
L'existence d'un identifiant unique pour chaque jeu de valeurs d permet la mise en œuvre d'une recherche par identifiant (ou recherche par clé).
La recherche par identifiant repose sur une fonction d'adressage I (dite "Index") qui à tout identifiant k associe sa position (entrée) i dans un tableau de données: I:k→i. Ainsi si k est l'identifiant servant à la recherche, l'extraction des informations se fait en 2 étapes:
L=(k1,k2,...,kN) telle que k1<k2<...<kN, de telle sorte que la recherche dans cette liste s'effectue en O(log N) (recherche dichotomique).
Définir un ordre sur les données
La présence d'un identifiant permet de définir un ordre total sur les données :
Lier les données
Dans le cas des bases de données, l'identifiant constitue une référence vers les jeux de valeurs des tableaux de données. Une référence permet de lier les données d'une table aux données d'une autre table.
Exemple :
Structure d'ensemble
L'index définit l'appartenance d'une donnée à un ensemble.
Soit E un ensemble de données indexées : E={d1,d2,...,dK} On a l'équivalence : d∈E⇔k(d)∈I
Ainsi, il ne peut y avoir de doublon car ∀d :
On introduit dans ce chapitre le modèle relationnel qui sert de fondation à la conception de bases de données.
Les différents modèles utiles en représentation des connaissances reposent sur des notions de base de la théorie des ensembles :
2 approches en modélisation :
Un tableau de données est une liste (finie et ordonnée) de tuples, chaque tuple obéissant à un même schéma R.
Rappel:
Soit R=(A1,...,Am) un schéma de données
Une relation r obéissant au schéma R est un sous-ensemble du produit cartésien dom(A1)×...×dom(Am)
UML :
Client(nom, prénom, adresse, âge)
Étudiant(nom, prénom, adresse, INE)
Ouvrage(titre, auteur, éditeur, prix, date_édition)
Véhicule(immatriculation, marque, modèle, couleur)
Soit la relation r:
A | B | C |
---|---|---|
1 | a | e |
2 | b | f |
2 | c | f |
3 | d | k |
4 | d | k |
F={num_client, date→num_article, quantité, prixnum_article, quantité→prix}
La contrainte signifie :
Exprimer la dépendance fonctionnelle :
Soit le schéma relationnel suivant :
Billet(num_train, type_train, num_voiture, num_place, date, id_passager, nom_passager, prénom_passager, date_naissance, gare_départ , horaire_départ, gare_arrivée, horaire_arrivée, classe, tarif)
Définir des dépendances fonctionnelles sur cet ensemble d'attributs
Représentation UML :
Soit K une clé candidate. On démontre que K→R à l'aide des axiomes d'Amstrong à partir d'un ensemble de DF connues:
Soit le schéma relationnel suivant :
Billet(num_train, type_train, num_voiture, num_place, date, id_passager, nom_passager, prénom_passager, date_naissance, gare_départ , horaire_départ, gare_arrivée, horaire_arrivée, classe, tarif)
Montrer que l'ensemble {num_train, num_voiture, num_place, date, gare_départ} est une clé primaire du schéma?
Tables mal construites
Fournisseur(nom_f, composant_,adresse_f, prix)
–> Impossible de retrouver les prix pratiqués par les différents fournisseurs.
–> Il est possible de reconstruire la table initiale en effectuant une jointure entre ces 2 tables sur l’attribut nom_f
.
Les Formes normales
R(A1,...,Ai,...,An_,B1,...,Bj,...,Bm)
AiDFE→Bj A1,...,Ai,...,AnDFE→B1,...,Bj−1,Bj+1...,Bm
R1(A1,...,Ai,...,An_,B1,...,Bj−1,Bj+1...,Bm) R2(Ai_,Bj)
Même si aucun attribut ne dépend plus de la clé primaire initiale, il est important de la conserver dans une table spécifique (elle sert à “lier” les valeurs dispersées dans les différentes tables).
Fournisseur(nom_f,composant_,adresse_f, prix) nom_f→adresse_f nom_f, composant→prix
Catalogue(nom_f,composant_,prix) Fournisseur(nom_f_,adresse_f)
La dépendance fonctionnelle entre 2 attributs Ai et Aj est directe s’il n’existe pas de Ak tel que : Ai→Ak→Aj
Un schéma est 3FN :
Commande(num_commande_,nom_f, adresse_f, composant, quantité) num_commande→nom_f, composant, quantité nom_f→adresse_f
Le schéma n’est pas 3FN!! (dépendance transitive entre num_commande et adresse)
* Lorsqu’un schéma relationnel n’est pas en troisième forme normale, il doit être normalisé:
Soit : R(A1,...,Am_,B1,...,Bi,...,Bj,...,Bn) avec : A1,...,AmDFD→B1,...,Bi,...,Bj−1,Bj+1,...,Bn BiDFD→Bj Alors : R1(A1,...,Am_,B1,...,Bi,...,Bj−1,Bj+1,...,Bn) R2(Bi_,Bj)
Comme précédemment, il est important de conserver la clé primaire de la table initiale si elle permet d'associer les valeurs dispersées dans les tables.
Avant :
Après :
L’attribut nom_f est maintenant clé primaire de la table Client et clé étrangère de la table Commande.