Fortran:  Input/output su file e format

  Nel caso di semplici programmi Fortran si può  effettuare input/output anche solo attraverso le   operazioni di I/O list directed  ( p.es:  read*, lista_variabili  o  print*,lista_espressioni  o ancora write(unit=1,fmt=*)lista_espressioni). In queste, l' input  proviene dal  cosiddetto standard input (tastiera, per programmi interattivi ), l' output va sullo standard output (schermo per programmi interattivi)  o sull' unità  logica n.1 (vedi dopo), e la conversione, tra rappresentazione interna dei dati e rappresentazione mediante caratteri o viceversa, viene svolta sulla base di scelte generali, fatte da chi ha disegnato il compilatore, rispettando le richieste dello Standard. Si tratta di default comodi per ottenere una rapida visualizzazione di risultati. Se però si desidera avere pieno controllo sui dispositivi di input-output (I/O) e sulla conversione tra rappresentazione interna e caratteri,  si possono utilizzare alcune istruzioni  di cui qui di seguito daremo un quadro sintetico, rimandando ai manuali per maggiori dettagli.
Da ricordare che  normalmente ( a meno di non scegliere la modalità  di access='stream') il Fortran considera i file come dotati di una struttura interna fatta da sottounità: i record.  Nella visualizzazione su schermo si può pensare il record come coincidente con una riga. L' inizio e la fine di ciascun record sul file vengono denotati attraverso l' inserimento di opportuni caratteri speciali. Ogni istruzione di lettura/scrittura inizia da un nuovo record.



Associazione con file esterni.

L' istruzione open  permette di definire modalità di accesso ad un file, interfacciando il programma col filesystem. In una versione di base, l' istruzione potrebbe avere 2  specificatori:

open(unit=u,
file=filename)  

Dove:
u: numero intero non negativo (e in genere diverso da 0, 5 e 6) che rappresenta l' indicatore del file nelle istruzioni di I/O (l' unit
à logica connessa al file). Sui sistemi Unix il nome risultante per i file così  controllati è fort.u (sostituire a u l' intero corrispondente) se non si associa un nome esplicito mediante l'argomento file;

filename    è una stringa (variabile di tipo character di lunghezza opportuna) che denota il nome del file nel filesystem utilizzato (nome corto o  nome lungo). Questo parametro deve essere assente se si utiliza lo status='scratch' (vedi dopo).

Esempio:
   open(unit=1, file='posizioni.dat')
farà in modo che le operazioni di scrittura relative a unit 1 corrispondano a scrivere sul file di nome posizioni.dat.
Altri possibili parametri sono:

iostat=ios  ios: variabile intera di default.    Il valore zero indica che l' apertura del file  è riuscita mentre un valore diverso da zero segnala errore.
iomsg=msg  msg: variabile di tipo character che conterrà una frase di descrizione del tipo di errore.
 
action=act  act
: stringa che specifica le azioni di trasferimento permesse per l' unità in questione. Possibili valori: "read" (solo lettura, non sono possibili istruzioni di scrittura), "write" (solo scrittura, no operazioni di lettura) e "readwrite" (sia scrittura sia lettura). Il default dipende dall' implementazione;

access=acc  acc: stringa che specifica il tipo di file Fortran associato all' unità.  I possibili tipi sono: "sequential" (il default, ogni record viene acceduto in sequenza dopo tutti i precedenti), "direct" (i record sono numerati e possono essre letti o scritti in qualsiasi ordine, senza passare per i record precedenti) e "stream" (senza record; il file corrrisponde ad una sequenza non strutturata di byte);

status=s  s: stringa che specifica lo stato del file connesso all' unità logica. Possibil valori: "old" (il file deve gi
à esistere), "new" (il file non deve esistere, viene creato dall' istruzione open), "scratch" (il file verrà creato e connesso all' unità logica ma  sarà cancellato all' istruzione close o al termine del programma), "replace" (il file viene creato se non esiste ma viene cancellato e ricreato se già esistente), "unknown" (il default, dipendente dall' implementazione del compilatore).

Altri parametri opzionali (far riferimento ad un manuale per la lista completa)

form=fm 
dove fm è una stringa che denota se il file è  formattato (fm="formatted" ) o non formattato (fm="unformatted" ). Un file è formattato quando i dati in esso contenuti sono stati scritti sotto forma di caratteri. Un file non formattato invece contiene dati non convertiti, nella stessa rappresentazione  usata internamente in memoria. Il default è formatted per i file sequenziali e unformatted per i file ad accesso diretto.

recl=rl     dove rl è un intero che esprime la  lunghezza di un record  (la massima lunghezza per file sequenziali;  è  obbligatorio per file ad accesso diretto).


L' istruzione close(unit=u)  o  
close(u)  chiude  l' associazione tra unità logica u e file esterno. Permettendo, tra l' altro, il riutilizzo del numero di unità logica per la connessione con un nuovo file.


Istruzioni I/O con formato esplicito

Soprattutto nella scrittura  può   essere utile, nel caso di file formattati, poter agire direttamente sulla conversione dei dati dalla rappresentazione interna a quella sotto forma di stringhe di caratteri.

Una specifica di formato è una stringa che va assegnata alla keyword fmt nelle istruzioni di scrittura (anche in quelle di lettura, volendo, ma per queste si rimanda  ad un manuale sul linguaggio).

I principali descrittori di formato sono:


Ciascun descrittore può essere preceduto da un intero che indica  il numero di dati a cui va applicato  quel descrittore. Esempio:

write(unit=1, fmt="(tr15,f10.2,i5,2es16.6)")  a,b,c,d

scrive sulla riga di output 4 variabili. la prima (a) deve essere reale (descrittore f10.2) e viene scritta in un campo totale di 10 caratteri, di cui 2 dedicati alle cifre a destra del punto decimale. Il numero viene scritto dopo aver lasciato 15 spazi  a destra del carattere di inizio del record (tr15). Il secondo dato (b) deve essere  un intero (descrittore i5) scritto in un campo di ampiezza 5 caratteri ed infine ci devono essere due  reali (c e d) che saranno scritti in notazione scientifica (mantissa ed esponente di 10) in un campo di complessivi 16 caratteri ciascuno (incluso il campo per l' esponente) di cui 6 dedicati alla parte decimale della mantissa del numero.

esempi  di uso del formato ES
valore
format
output
21.215
es14.6
  2.121500e+01
82321.3
es10.2
   8.23e+4
-0.53
es12.2
    -5.30e-01
0.000229
es14.1e4
     2.3e-0004


Nelle situazioni in cui non si vuole controllare direttamente il formato è sufficiente usare il parametro fmt=*. Attenzione: si tratta sempre di I/O formattato, solo la trasformazione da/verso caratteri viene controllata da decisioni prese da chi ha disegnato il compilatore. Il nome tecnico per tale situazione è di "formato controllato dalla lista" (list directed format), nel senso che è la lista delle espressioni o delle variabili che controlla, attraverso il tipo dati di ogni elemento, il tipo di conversione.

Input/output non formattato

Se l' unità  di I/O è  stata aperta in modalità  unformatted  o, in assenza di open, se l' istruzione read o write  non contengono il parametro fmt=...,  l' I/O avviene in modalità  unformatted.
Es:

...
real, dimension(10000) :: x
...
write(unit=10)x
...
crea un file di nome fort.10  (sistemi unix)   contenente le componenti dell' array x nella rappresentazione del sistema per numeri "real".

Controllo delle condizioni di errore

Durante le istruzioni di I/O è  possibile gestire eventuali errori via software a partire dal parametro iostat.
Dichiarando una variabile intera di default (ios), un' istruzioni come:
read(unit=1,fmt=*,iostat=ios)x,y,z
ritorna attraverso il valore di ios informazioni su eventuali condizioni di errore in lettura. In particolare:
ios=0  indica lettura andata a buon fine
ios<0  indica le condizioni di end-of-file oppure end-of-record (per file ad accesso diretto)
ios>0  indica condizioni di errore generate da errori nell' indicazione del formato (per I/O formattato non list-directed)
Un esempio di utilizzo di questo parametro per contare i record (o linee) di un file è  il seguente

...
integer :: ios
...
lines=0
do
read(unit=1,fmt='(A)',iostat=ios)
if(ios < 0)exit
lines=lines+1
end do
rewind 1 ! riposiziona l' unità 1 all' inizio per poter eventualmente leggere il
! contenuto del file sapendo qual è il numero dei record/linee.
...