Input and output – Second part
Entrée et sortie standard
Ce sont deux «flux» automatiquement disponibles à l'exécution d'un programme en Fortran. On utilise ces flux via les instructions :
read *, …
et :
print *, …
Cf. Input and output – First part.
Clavier et écran
Si on lance le programme avec la simple instruction :
./my_program
alors l'entrée standard est connectée au clavier et la sortie standard est connectée à l'écran.
Redirection
Rappel du cours sur Unix : possibilité de «rediriger» l'entrée standard ou la sortie standard. Exemples :
./my_progr <inp_file
./my_progr >outp_file
./my_progr <inp_file >outp_file
Intérêt d'ouvrir des fichiers dans le programme en Fortran
-
Pour avoir plus d'un flux en lecture ou en écriture. Exemples :
- séparer la réponse à des questions (clavier) et la lecture de tableaux de nombres (fichier)
- séparer des messages d'avertissement (écran) et des écritures de
tableaux de nombres (fichier)
- Pour pouvoir rembobiner (c'est-à-dire relire), ce qui est impossible sur l'entrée standard. Important pour lire des tableaux de taille inconnue.
Différents types d’entrées-sorties
L'accès peut être séquentiel, direct ou stream.
Par ailleurs, la lecture ou l'écriture de données peuvent être:
- formatted : données sous forme textuelle
- ou unformatted : données comme stockées en mémoire vive
Ce cours : accès séquentiel, format texte. Le plus utile.
Les autres types d’entrées-sorties sont très rarement utiles directement (ils sont très utiles mais de façon cachée via des bibliothèques, comme NetCDF, si bien que vous n’utilisez pas directement les instructions Fortran correspondantes).
Caractéristiques de l'accès séquentiel, format texte :
-
Notion de « position courante » dans le fichier. On ne peut lire ou écrire qu'à la position courante. Pour aller d'une position à une autre, plus loin, obligation de lire tous les enregistrements entre les deux positions.
-
Possibilité d'écrire seulement à la fin du fichier. (→ possibilité de récrire seulement le dernier enregistrement)
Numéro d'unité
- Un fichier a un nom, l'instruction
open
associe un “numéro d'unité” à ce nom de fichier. - Numéro d'unité : nombre entier ≥ 0.
- Le numéro d’unité est un argument d’entrée de l'instruction
open
. - L'ensemble des numéros autorisés dépend du compilateur. Conseil :
utiliser la procédure
new_unit
de la bibliothèque Jumble pour trouver un numéro autorisé et disponible. - L'association numéro d'unité – fichier persiste dans tout le
programme, entre le moment du
open
et le moment de la fermeture du fichier (instructionclose
, cf. plus loin). On peut par exemple ouvrir un fichier dans une procédure et faire une lecture de ce fichier dans une autre procédure : il suffit de transmettre le numéro d'unité.
Création d'un fichier
INTEGER unit
…
CALL NEW_UNIT(unit)
OPEN(unit, FILE = "…", STATUS = "REPLACE", ACTION = "WRITE")
- La procédure
new_unit
trouve une valeur convenable de numéro d'unité et met cette valeur dans son argument. - L'instruction
open
détruit le fichier s'il existait déjà (c'est l'effet de l'argumentstatus = "replace"
).
Écriture et fermeture du fichier :
WRITE(numéro d'unité, FMT = *) liste d'expressions
CLOSE(numéro d'unité)
- Nota bene : ces instructions font référence au fichier via son numéro d'unité.
- L’instruction de fermeture est obligatoire.
Exemple d'écriture :
integer unit
real x(3), y, z
…
call new_unit(unit)
open(unit, file = "plouf.txt", status="replace", action="write")
write(unit, fmt=*) x
write(unit, fmt=*) y, z
close(unit)
Nombre de lignes écrites :
- Chaque instruction
write
écrit un retour à la ligne (après les valeurs écrites). - Une instruction
write
écrit un nombre de lignes quelconque, au choix du compilateur, selon le nombre d’expressions dans la liste.
Pour écrire une ligne vide :
WRITE(numéro d'unité, FMT=*)
(sans liste d'expressions)
Lecture d'un fichier
Déclarez une variable pour le numéro d'unité :
INTEGER unit
(par exemple, mais vous pouvez choisir n'importe quel nom de variable)
…
CALL NEW_UNIT(unit)
OPEN(unit, FILE="…", STATUS="OLD", ACTION="READ", POSITION="REWIND")
Le fichier ne pourra pas être modifié (les instructions write
dans
ce fichier seront interdites).
Exemple de lecture :
integer unit
real x(3), y, z
…
call new_unit(unit)
open(unit, file="plouf.txt", status="old", action="read", position="rewind")
read(unit, fmt=*) x
read(unit, fmt=*) y, z
close(unit)
Nombre de lignes lues :
- Chaque instruction
read
commence à une nouvelle ligne dans le fichier lu. -
Chaque instruction
read
lit un nombre de lignes quelconque : autant de lignes que nécessaire pour remplir toutes les variables de la liste. Exemple :REAL X(3) READ(numéro d'unité, FMT = *) X
Trois valeurs à lire. La lecture de lignes continue tant que 3 valeurs n'ont pas été trouvées.
-
Quand toutes les valeurs nécessaires ont été trouvées, les valeurs restantes sur la ligne en cours sont ignorées.
Pour sauter une ligne :
READ(numéro d'unité, FMT = *)
(sans liste de variables)
Rembobinage
REWIND(numéro d'unité)
pour revenir au début du fichier. Utilisation typiquement dans la séquence d'opérations :
- décompte des lignes d’un fichier
- allocation d'un tableau avec le nombre trouvé
- rembobinage
- remplissage du tableau à partir des valeurs sur les lignes
Namelist
Définition et intérêt
Conservation dans un fichier des entrées d'un programme, sous une forme lisible par un être humain et exploitable directement par le programme. Exemple : namelist pour le programme WRF (Weather Research and Forecasting Model).
- Une « namelist » est un groupe de variables.
- On peut utiliser une namelist pour une entrée ou une sortie.
Déclaration
Par exemple, on peut écrire dans un programme en Fortran :
real a, b
integer c(3)
namelist /coef/ a, b, c
Dans cet exemple, coef est le nom de la namelist et a, b, c sont les variables qui font partie de la namelist coef.
Instruction Fortran pour la lecture d'une namelist
Si les données sont lues sur l'entrée standard, on peut écrire par exemple, pour une namelist qui s'appellerait coef :
read(unit = *, NML = coef)
Si les données sont lues dans un fichier ouvert avec open
alors on
donne le numéro d'unité correspondant. Par exemple si unit est la
variable contenant le numéro d'unité :
read(unit, nml = coef)
Lecture d'une namelist à l'exécution
Les lignes lues doivent commencer par &
suivi du nom de la namelist
et finir par le caractère /
. Entre les deux, on doit entrer le nom
de chaque variable avec le signe égal et la valeur correspondante. Les
variables sont séparées par une virgule, des espaces ou un retour à la
ligne. Par exemple, on peut taper dans le terminal au moment de
l'exécution d'un programme :
&coef a = 1, b = 3 c = 0 /
Valeurs par défaut
Possibilité de ne définir à l'exécution qu'une partie des variables. Les variables non entrées conservent leur valeur antérieure. Conseil : toujours donner dans le programme des valeurs par défaut aux variables d'une namelist avant l'instruction de lecture.
Exemple avec valeurs par défaut. Dans le programme :
a = 1
b = 0
c = 0
read(unit=*, nml=coef)
L'utilisateur peut écrire seulement au moment de l'exécution, par exemple :
&coef b = -2 /
(dans le terminal)
Autres possibilités
- Ordre quelconque des variables à l'exécution. Par exemple, dans le programme :
namelist /coef/ a, b, c
Dans le terminal :
&coef b = 3, a = 5 /
- Modification d'une partie de tableau seulement à l'exécution. Par exemple, dans le programme :
integer a(10)
namelist /plouf/ a, b, x
a = 0 ! default value
Dans le terminal :
&plouf b = 5, a(3) = 1 /
- À l'exécution, ajout de commentaires après un point d'exclamation. Par exemple, dans le terminal :
&time_control
run_days = 1 ! run time in days
…
/
Namelist : conclusion
-
Il est intéressant de conserver dans un fichier la namelist utilisée pour une exécution donnée (pour mémoire, ou pour refaire la même exécution, ou comme base pour des variations de namelist).
-
Les namelists sont utilisées notamment pour les gros programmes scientifiques, qui peuvent fonctionner dans plusieurs configurations. Intérêt de disposer d'un fichier contenant les namelists pour une configuration donnée.