Descripteurs de fichiers
Au chapitre , nous avons vu les fonctions
standards du module Pervasives, elles permettent
d'accéder aux fichiers via des canaux d'entrées-sorties. Il existe
un moyen de plus bas niveau d'accès aux fichiers en ayant recours à
leur descripteur.
Un descripteur de fichier est une valeur abstraite, de type
Unix.file_descr, qui contient les informations nécessaires
à l'utilisation d'un fichier : un pointeur vers ce fichier, les
droits d'accès à ce fichier, les conditions d'accès au fichier
(lecture ou écriture), la position courante dans le fichier, etc.
Trois descripteurs sont prédéfinis. Ils correspondent aux fichiers
d'entrée standard, de sortie standard et d'erreur standard.
# (
Unix.stdin
,
Unix.stdout
,
Unix.stderr
)
;;
- : Unix.file_descr * Unix.file_descr * Unix.file_descr =
<abstr>, <abstr>, <abstr>
Attention à ne pas les confondre avec les canaux d'entrées-sorties
leur correspondant :
# (
Pervasives.stdin
,
Pervasives.stdout
,
Pervasives.stderr
)
;;
- : in_channel * out_channel * out_channel = <abstr>, <abstr>, <abstr>
Des fonctions de conversion entre canaux et descripteurs de fichier
sont décrites à la page X.
Droits d'accès aux fichiers
Sous UNIX, des droits en lecture,
en écriture et en exécution sont attachés à chaque
fichier. Ils concernent trois catégories
d'utilisateurs : le propriétaire du fichier, les membres du
groupe1 du propriétaire ou l'ensemble de tous les utilisateurs.
Les droits d'un fichier sont représentés par 9 bits divisés en
trois groupes de trois bits. Le premier groupe représente les droits
du propriétaire, le deuxième les droits des membre du groupe du
propriétaire et le dernier les droits des autres utilisateurs. Dans
chaque groupe de trois bits, le premier représente le droit en
lecture, le deuxième, le droit en écriture et le dernier, le droit
en exécution. On a coutume de représenter ces droits respectifs
par les lettres r, w et x ; et l'absence de droit par
un tiret (-). Par exemple, le droit en lecture pour tous et en
écriture pour le propriétaire seul s'écrit rw-r--r--. C'est, en fait, l'entier 420 (soit le binaire
0b110100100). On utilisera souvent la notation octale
0o644 qui est d'un emploi plus aisé. Ces droits sur les fichiers
ne sont pas utilisés sous WINDOWS.
Manipulation des fichiers
Ouverture d'un fichier
Ouvrir un fichier, c'est récupérer un descripteur de ce
fichier. Suivant l'usage que l'on veut en faire, il existe
plusieurs mode d'ouverture des fichiers. Chacun correspond à une
valeur du type open_flag décrit figure 3.2.
O_RDONLY |
en lecture |
O_WRONLY |
en écriture |
O_RDWR |
en lecture et en écriture |
O_NONBLOCK |
ouverture dans un mode non-bloquant |
O_APPEND |
en ajout à la fin du fichier |
O_CREAT |
crée le fichier s'il n'existe pas |
O_TRUNC |
remet le fichier à 0 s'il existe |
O_EXCL |
échoue si le fichier existe |
Figure 3.2 : Valeurs de type open_flag
Ces modes peuvent être combinés, en conséquence la fonction
openfile prendra en argument une liste de valeurs
open_flag.
# Unix.openfile
;;
- : string -> Unix.open_flag list -> Unix.file_perm -> Unix.file_descr =
<fun>
Le premier argument est le nom du fichier et le dernier est un
entier2 codant les permissions à donner au fichier en cas de
création. Voici, par exemple, comment ouvrir en lecture un fichier,
ou le créer avec les droits rw-r--r-- s'il n'existe pas :
# let
fichier
=
Unix.openfile
"essai.dat"
[
Unix.
O_RDWR;
Unix.
O_CREAT]
0
o644
;;
val fichier : Unix.file_descr = <abstr>
Fermeture d'un fichier
La fonction Unix.close permet de fermer un fichier. On
l'applique au descripteur du fichier que l'on désire fermer.
# Unix.close
;;
- : Unix.file_descr -> unit = <fun>
# Unix.close
fichier
;;
- : unit = ()
Redirection de fichiers
On peut attacher à une même entrée-sortie plusieurs
descripteurs. Si l'on dispose d'un seul descripteur et que l'on veut en
obtenir un nouveau, on utilisera :
# Unix.dup
;;
- : Unix.file_descr -> Unix.file_descr = <fun>
Si l'on dispose de deux descripteurs et que l'on veux réassigner au
second l'entrée-sortie correspondant au premier, on utilisera la
fonction :
# Unix.dup2
;;
- : Unix.file_descr -> Unix.file_descr -> unit = <fun>
Par exemple, on redirige la sortie d'erreur sur un fichier :
# let
sortie_erreur
=
Unix.openfile
"err.log"
[
Unix.
O_WRONLY;Unix.
O_CREAT]
0
o644
;;
val sortie_erreur : Unix.file_descr = <abstr>
# Unix.dup2
Unix.stderr
sortie_erreur
;;
- : unit = ()
Les écritures sur la sortie erreur standard sont maintenant envoyées
sur le fichier err.log.
Entrées-sorties sur un fichier
Les fonctions de lecture et d'écriture sur un fichier,
respectivement Unix.read et Unix.write, utilisent une
chaîne de caractères comme médium entre le fichier et le
programme Objective CAML.
# Unix.read
;;
- : Unix.file_descr -> string -> int -> int -> int = <fun>
# Unix.write
;;
- : Unix.file_descr -> string -> int -> int -> int = <fun>
Outre le descripteur de fichier et la chaîne, les fonctions
prennent deux entiers en arguments qui sont l'indice du premier
caractère et le nombre de caractères lus ou écrits. L'entier
retourné est le nombre de caractères effectivement lus ou écrits.
# let
mode
=
[
Unix.
O_WRONLY;Unix.
O_CREAT;Unix.
O_TRUNC]
in
let
fic
=
Unix.openfile
"fichier"
mode
0
o644
in
let
str
=
"012345678901234565789"
in
let
n
=
Unix.write
fic
str
4
5
in
Printf.printf
"On a écrit %s dans notre fichier\n"
(String.sub
str
4
n)
;
Unix.close
fic
;;
On a écrit 45678 dans notre fichier
- : unit = ()
La lecture procède du même principe :
# let
fic
=
Unix.openfile
"fichier"
[
Unix.
O_RDONLY]
0
o644
in
let
str
=
String.make
2
0
'.'
in
let
n
=
Unix.read
fic
str
2
1
0
in
Printf.printf
"On n'a lu que %d caractères"
n;
Printf.printf
" et on obtient la chaîne %s\n"
str;
Unix.close
fic
;;
On n'a lu que 5 caractères et on obtient la chaîne ..45678.............
- : unit = ()
Un accès à un fichier se fait toujours à la position courante contenue
dans son descripteur. On peut cependant modifier cette position
grâce à la fonction :
# Unix.lseek
;;
- : Unix.file_descr -> int -> Unix.seek_command -> int = <fun>
Le premier argument est le descripteur du fichier, le second est la
taille, en caractères, du déplacement et le troisième argument,
de type Unix.seek_command, indique l'origine. Ce dernier
argument a trois valeurs possibles :
-
SEEK_SET : relativement au début du fichier,
- SEEK_CUR : relativement à la position courante,
- SEEK_END : relativement à la fin du fichier.
Une position erronée provoque soit le déclenchement d'une
exception, soit une valeur de retour égale à 0 suivant les
fonctions d'entrée-sortie utilisées.
Canal d'entrée-sortie
Les canaux d'entrées-sorties, définis dans le module Pervasives,
peuvent être convertis en descripteur de fichiers et réciproquement.
Pour ce faire on a recours aux fonctions de
conversions :
# Unix.in_channel_of_descr
;;
- : Unix.file_descr -> in_channel = <fun>
# Unix.out_channel_of_descr
;;
- : Unix.file_descr -> out_channel = <fun>
# Unix.descr_of_in_channel
;;
- : in_channel -> Unix.file_descr = <fun>
# Unix.descr_of_out_channel
;;
- : out_channel -> Unix.file_descr = <fun>
Il est nécessaire de préciser si les canaux d'entrée-sortie
transfèrent des données binaires ou des caractères :
# set_binary_mode_in
;;
- : in_channel -> bool -> unit = <fun>
# set_binary_mode_out
;;
- : out_channel -> bool -> unit = <fun>
Dans l'exemple suivant on crée un fichier en utilisant les fonctions
du module Unix et on le lit en utilisant la fonction
d'ouverture du module Unix et la fonction d'entrée de plus
haut niveau input_line.
# let
mode
=
[
Unix.
O_WRONLY;Unix.
O_CREAT;Unix.
O_TRUNC]
in
let
f
=
Unix.openfile
"fichier"
mode
0
o666
in
let
s
=
"0123456789\n0123456789\n"
in
let
n
=
Unix.write
f
s
0
(String.length
s)
in
Unix.close
f
;;
- : unit = ()
# let
f
=
Unix.openfile
"fichier"
[
Unix.
O_RDONLY;Unix.
O_NONBLOCK]
0
in
let
c
=
Unix.in_channel_of_descr
f
in
let
s
=
input_line
c
in
print_string
s
;
close_in
c
;;
0123456789- : unit = ()
Disponibilité
Un programme peut avoir à gérer une
multiplicité d'entrées-sorties. Celles-ci ne sont pas toujours
disponibles : d'autres programmes on pu en réclamer l'usage. La
fonction :
# Unix.select
;;
- : Unix.file_descr list ->
Unix.file_descr list ->
Unix.file_descr list ->
float ->
Unix.file_descr list * Unix.file_descr list * Unix.file_descr list
= <fun>
permet de connaître la liste des entrées-sorties disponibles à un
moment donné parmi une liste donnée. Ses trois premiers arguments
représentent, respectivement, les listes des entrées (lecture) ,
des sorties (écriture) et des sorties en erreur. Le dernier
argument indique un délai d'attente en secondes (une valeur
négative indique un délai nul). Le résultat est la liste des
entrées-sorties disponibles en lecture, écriture et erreur.
Warning
select n'est pas implantée pour WINDOWS