Descripteurs de fichier
Au chapitre 3, nous avons vu les fonctions
standard 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 ??.
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 membres 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 modes d'ouverture des fichiers. Chacun correspond à une
valeur du type open_flag décrit figure 18.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 18.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
de type 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 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 veut 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 détournées vers le
fichier err.log.
Entrées-sorties sur un fichier
Les fonctions de lecture et d'écriture sur un fichier
Unix.read et Unix.write utilisent une chaîne de
caractères comme média 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 argument qui sont l'indice du premier
caractère et le nombre souhaité de caractères à lire ou à écrire. 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 le fichier\n"
(String.sub
str
4
n)
;
Unix.close
fic
;;
On a écrit 45678 dans le 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ées-sorties utilisées.
Canal d'entrée-sortie
Le module Unix fournit des fonctions de conversion entre les
descripteurs de fichier et les canaux d'entrées-sorties du module
Pervasives :
# 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ées-sorties
obtenus par conversion 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. Or celles-ci ne sont pas toujours
disponibles : d'autres programmes ont pu précédemment en réclamer
l'usage. La fonction suivante permet de connaître la liste des
entrées-sorties disponibles à un moment donné parmi une liste
donnée :
# 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>
Les trois premiers arguments représentent les listes respectivement
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