Skip to content

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.

Entrée et sortie standard

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.

Clavier et é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

Redirection

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) Plus d'un flux en lecture ou en écriture
    • 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.

    Cassette VHS

  • Possibilité d'écrire seulement à la fin du fichier. (→ possibilité de récrire seulement le dernier enregistrement)

    Possibilité d'écrire seulement à la fin du fichier

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 (instruction close, 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
(par exemple, mais vous pouvez choisir n'importe quel nom de variable)

…
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'argument status = "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.