We onderscheiden twee soorten I/O:
WRITE (UNIT=nn) A, B, C, ...
READ (UNIT=nn) A, B, C, ...
REWIND nn
Hier is ``nn'' een integer expressie die het unit nummer geeft
0 <= nn <= 99
De variabelen A, B, C, ... kunnen variabelen,
array elementen, hele arrays, of ``implied DO lists'' (zie later) zijn.
Ieder WRITE statement definieert een record. Records worden
achter elkaar (sequentieel) in een file geschreven. Als de file
volledig geschreven is kan hij met een REWIND teruggespoeld worden
naar het begin en dan kan de file gelezen worden met READ
statements. Een nieuwe file kan een DOS naam gegeven worden door
vóór executie van het programma het DOS kommando SET te geven.
Bijvoorbeeld voor unit 1:
SET 1=TEST.DAT
Als men geen SET doet wordt door WATFOR77 een file FTnnF001 aangemaakt,
waar ``nn'' het unit nummer is.
Voorbeeld:
DIMENSION A(100), B(100)
C INITIALISEER ELEMENTEN VAN ARRAY A EN B.
DO 10 I=1,100
A(I) = I
B(I) = 0.E0
10 CONTINUE
C SCHRIJF RECORDS VAN TWEE ELEMENTEN ELK.
DO 20 I=1,100,2
WRITE (UNIT=1) A(I), A(I+1)
20 CONTINUE
C GA TERUG NAAR BEGIN FILE
REWIND 1
C LEES RECORDS TERUG IN ARRAY B
DO 30 I=1,100,2
READ (UNIT=1) B(I), B(I+1)
30 CONTINUE
C SCHRIJF WAT VAN B NAAR SCHERM TER CONTROLE
C (UNIT 6 IS GEWOONLIJK SCHERM)
DO 40 I=1,100,10
WRITE (UNIT=6, FMT=*) B(I)
40 CONTINUE
C
END
Omdat er 50 WRITE statements uitgevoerd zijn staan er dus na
uitvoering van bovenstaande programma 50 records op de file verbonden
met unit 1. De lengtes van de achtereenvolgende records worden bepaald
door het aantal variabelen in ieder WRITE statement (in
bovenstaande voorbeeld zijn alle records acht bytes lang).
De records mogen van verschillende lengte zijn, maar dan wordt het teruglezen lastiger. Er zijn drie mogelijkheden:
READ (UNIT=nn, END=lll) A, B, C, ....
Als het einde van het file bereikt wordt, springt het programma naar
het statement met label ``lll''.
Voorbeeld:
DIMENSION A(100), B(100)
C INITIALISEER ARRAY A
DO 10 I=1,100
A(I) = I
10 CONTINUE
C SCHRIJF RECORDS VAN VARIABELE LENGTE.
WRITE (UNIT=99) A(1)
WRITE (UNIT=99) A(2), A(10)
WRITE (UNIT=99) A(3), A(4), A(7)
WRITE (UNIT=99) A(4), A(5), A(8), A(9)
WRITE (UNIT=99) A(5)
C GA TERUG NAAR BEGIN FILE
REWIND 99
C LEES EERSTE 4 BYTES VAN ELK RECORD TERUG IN ARRAY B
I = 0
20 I = I+1
READ (UNIT=99, END=30) B(I)
GO TO 20
C
30 N = I-1
DO 40 I=1,N
WRITE (UNIT=6, FMT=*) I, B(I)
40 CONTINUE
C
END
In de READ loop worden in totaal vijf records gelezen, omdat er
eerder vijf weggeschreven zijn. In de eerste leesopdracht wordt precies
het hele record gelezen, in de tweede worden de eerste vier bytes van het
record gelezen en de tweede vier bytes geskipt, in de derde READ
worden acht bytes geskipt, enz. Men kan proberen om afwisselend in een
sequentiële file te lezen en te schrijven, maar zodra men een record
schrijft in een bestaande file zijn alle records er achter verloren. Men
moet dus altijd aan het einde van de file toevoegen.
In FORTRAN heeft de programmeur de mogelijkheid het formatteringsproces te sturen. Dit wordt nu behandeld.
WRITE, READ en REWIND zijn weer de elementaire I/O statements. Syntax:
WRITE (UNIT=nn, FMT=lll) A, B, C, ...
READ (UNIT=nn, FMT=lll, END=kkk) A, B, C, ...
REWIND nn
lll FORMAT ( .... )
Hierin is ``nn'' het unit nummer, A, B, C, ... zijn FORTRAN variabelen
(inclusief arrays, implied DO lists en constanten) en ``kkk'' is
het label van een statement waarheen gesprongen wordt als de READ
voorbij het einde van het file probeert te lezen. Nieuw is ``FMT=lll'' in
het READ en WRITE statement. In het algemeen is ``lll'' het label van een
FORMAT statement.
Eerder hebben we gezien dat ``lll'' ook een ster ``*'' kan zijn, in welk geval FORTRAN zelf uitmaakt op welke manier A, B, C, ... geformatteerd zullen worden. Het gebruik van dit standaard format (list-directed I/O) heeft als nadeel dat het moeilijk is om nette output (tabellen enz.) te schrijven. Hiervoor is het nodig om iets van formattering te weten. In het verleden werden data ook vaak geformatteerd ingelezen. Dit is vrij gebruikersonvriendelijk, en daarom hebben de meeste programma's tegenwoordig manieren om dit te vermijden.
Het lezen en schrijven van een integer gaat met formatcode ``In'',
waarin ``n'' een geheel getal is dat het aantal cijfers aangeeft dat
ingelezen of geschreven wordt. Bijvoorbeeld:
WRITE (UNIT=5, FMT=500) INT
500 FORMAT (I5)
FORTRAN schrijft de variabele INT rechts aangelijnd in de eerste vijf
posities van de regel. Dus voor de volgende waarden van INT krijgen we
12345 12345
INT = 1 ---> 1 INT = -1 ---> -1
INT = 12 ---> 12 INT = -12 ---> -12
INT = 123 ---> 123 INT = -123 ---> -123
INT = 12345 ---> 12345 INT = -1234 ---> -1234
INT = 123456 ---> ***** INT = -12345 ---> *****
Als de waarde van INT niet in het format past (in dit voorbeeld
als INT > 99999 of INT < -9999) krijgen we even zoveel
sterren te zien als het veld breed is.
In het algemeen is het verstandig de eerste positie van een output record blank te laten (spatie in positie 1), omdat lijnprinters dat eerste character als een besturingscharacter (het zg. carriage control character) interpreteren. Met dit eerste character kan men een lijnprinter bijvoorbeeld lege pagina's laten genereren of stil laten staan (waardoor regels over elkaar gedrukt worden). Het teken ``space'' (ASCII 32, de spatie) in de eerste positie vertelt de printer naar de volgende regel te gaan.
Formatcode ``nX'' geeft aan dat er ``n'' spaties in het record staan (bij invoer) of komen (bij uitvoer). Deze code kan gebruikt worden om uitvoer overzichtelijk te presenteren.
Voor character data gebruikt men formatkode ``An'' (A staat voor alphanumeric).
Voorbeeld:
WRITE (UNIT=6, FMT=600) 'AAP NOOT MIES'
600 FORMAT (1X, A13)
Dit schrijft de tekst voorafgegaan door een blank naar unit 6
(meestal de printer of het scherm).
Nu volgt een voorbeeld van een geheel programma met diverse
geformatteerde I/O statements. In dit programma laten we de keywords
UNIT en FMT weg in I/O statements.
Dit is toegestaan, mits we beide tegelijk weglaten.
DIMENSION INT(10)
CHARACTER*35 ERROR
ERROR = 'TE VEEL VARIABELEN, PROGRAMMA STOPT'
IA = 10
WRITE (6, 600) 'VOER GEHELE GETALLEN IN'
600 FORMAT (1X, A23)
C READ INTEGERS VAN TOETSENBORD TOT 'END OF FILE'
I = 0
10 CONTINUE
I = I + 1
READ (5, 510, END=20) INT(I)
510 FORMAT (I5)
C LEES ALLEEN VOLGENDE GETAL ALS WE NIET UIT ARRAY LOPEN
IF (I .GE. IA) THEN
WRITE (6, 610) ERROR
610 FORMAT (1X, A35)
STOP
ENDIF
GOTO 10
C KLAAR MET LEZEN, GA SCHRIJVEN
20 CONTINUE
N = I - 1
WRITE (6, 620) 'AANTAL INGELEZEN GEGEVENS', N
620 FORMAT (1X, A21, 4X, I3)
C
DO 30 I=1,N
WRITE (6, 630) INT(I)
630 FORMAT (1X, I8)
30 CONTINUE
C
END
Een FORMAT statement mag character konstanten bevatten. In het
algemeen zal men dit liever gebruiken dan het ``An'' format, omdat men dan
niet hoeft te tellen. De volgende twee voorbeelden geven exakt
dezelfde uitvoer:
WRITE (6, 610) 'AANTAL INGELEZEN DATA', N
610 FORMAT (1X, A21, 4X, I3)
en:
WRITE (6, 610) N
610 FORMAT (1X, 'AANTAL INGELEZEN DATA', 4X, I3)
Voor de uitvoer van floating point data (reële getallen met drijvende
komma) bestaan twee formatmogelijkheden. De eerste mogelijkheid gebruikt
men in het algemeen als men niet weet hoe groot de getallen zijn, of als
ze erg ver in absolute waarde van 1 liggen. Dit format heeft de vorm
``El.d'', waarin ``l'' de lengte van het veld is en ``d'' aantal
decimale cijfers (cijfers achter de punt). Dit format geeft bij uitvoer
een grondtal en een macht van 10, zodat het grondtal tussen .1 en .9
ligt. Bijvoorbeeld voor de dubbele precisie variabele
A = -0.123456789012345D-3 = -0.000123456789012345 krijgen we
de volgende output:
12345678901234567890
E13.6 ---> -0.123457E-03
E13.7 ---> -.1234568E-03
E13.8 ---> *************
E20.1 ---> -0.1E-03
E20.9 ---> -0.123456789E-03
Merk op:
Als de absolute waarden van de floating point getallen niet te ver
van 1 liggen, is ``Fl.d'' een handig format. Hierin is ``l'' weer de
lengte van het veld, en ``d'' het aantal cijfers achter de punt. In
dit geval krijgen we geen macht van 10 te zien en hoeven dus niet
de vier characters voor de exponent te reserveren. Stel
A = 123.456789012345D0, dan krijgen we de volgende output:
12345678901234567890
F13.6 ---> 123.456789
F13.7 ---> 123.4567890
F13.10 ---> *************
F20.12 ---> 123.456789012345
F10.6 ---> 123.456789
De invoer van floating point getallen heeft erg veel vrijheid. Men kan
binnen het FORTRAN programma in het format statement ``Fl.d'' hebben,
en in de invoer toch een exponent, en ook omgekeerd: als het format
``El.d'' is, hoeft in de input toch geen exponent te staan. In het
algemeen zal men in de invoer de punt expliciet meegeven, hoewel dit
niet hoeft.
WRITE (6, 600) I1, I2, I3, I4, I5
600 FORMAT (1X, I3, I3, I3, I3, I3)
maar dit is omslachtig. Elk format kan voorafgegaan worden
door een positief geheel getal, dat zegt hoe vaak het format
herhaald moet worden. Dus het bovenstaande kan worden:
WRITE (6, 600) I1, I2, I3, I4, I5
600 FORMAT (1X, 5I3)
Let op het verschil in definitie van ``n'' en ``m'' in
de format specificatie ``nIm'': ``n'' maal een integer van ``m'' cijfers.
Een repetitiefactor mag ook een groep van formatspecificaties herhalen,
bijvoorbeeld
FORMAT (2(2X, I3, ',', F10.5)) = FORMAT (2X, I3, ',', F10.5, 2X,I3, ',', F10.5)
Merk op dat we hier de character konstante `` ',' '' hebben. Een
groep formatspecifikaties is handig bij het schrijven van veel
variabelen (een array bijvoorbeeld). Wanneer het meest rechtse haakje
van het format bereikt wordt en nog niet alle variabelen zijn
geschreven, wordt een nieuw output record (een nieuwe regel) begonnen en
het format keert terug naar het openingshaakje behorend bij het
één na laatste sluithaakje. Als voor dit openingshaakje
een repetitiefactor staat doet dit opnieuw mee.
Voorbeeld (let op formats met labels 510, 520 en 640):
DOUBLE PRECISION A(3),B(2)
CHARACTER*60 TITLE
10 WRITE (6, 600)
600 FORMAT (' ENTER TITLE OF RUN, (A60)')
READ (5, 500, END=20) TITLE
500 FORMAT (A60)
WRITE (6, 610)
610 FORMAT (' ENTER 3 A-WAARDEN, (F10.6)')
READ (5, 510, END=20) A(1), A(2), A(3)
510 FORMAT (3F10.6)
WRITE (6, 620)
620 FORMAT (' ENTER 2 B-WAARDEN, (E15.8)')
READ (5, 520, END=20) B(1), B(2)
520 FORMAT (2E15.8)
WRITE (6, 630) TITLE
630 FORMAT (1X, A60)
WRITE (6, 640) A, B
640 FORMAT (1X, 5E15.6)
GO TO 10
20 END
( array(i), i=ondergrens, bovengrens )
Ook (stukken uit) twee- of meer-dimensionale arrays kunnen zo
gelezen of geschreven worden. Dit gaat door te nesten.
Bijvoorbeeld (2-dimensionaal):
READ (5, 500) ( (ARRAY(I,J), J=JMIN,JMAX), I=IMIN,IMAX )
De binnenste loop over J wordt herhaald voor alle in de buitenste
loop gespecificeerde I waarden; in totaal worden
(JMAX-JMIN+1)*(IMIN-IMAX+1) elementen van ARRAY ingelezen.
Voorbeeld:
DIMENSION A(100), B(-10:10,-10:10)
C VUL A:
DO 10 I=1,100
A(I) = REAL(I)
10 CONTINUE
C B WORDT HIER WAT MERKWAARDIG INGEVULD OM TE LATEN ZIEN DAT EEN
C EEN INCREMENT OOK NEGATIEF MAG ZIJN:
DO 30 I=-10,10
DO 20 J=10,-10,-1
B(I,J) = REAL(I+J)
20 CONTINUE
30 CONTINUE
C DE GRENZEN VOOR DE IMPLIED DO LISTS:
JMIN = -3
JMAX = +3
IMIN = -5
IMAX = 0
C NU KOMEN TWEE IMPLIED DO LISTS, LET OP HET GEBRUIK VAN DE
C REPETITIEFACTOR VOOR EEN GROEP
WRITE (6, 600) (A(I), I=31,41)
600 FORMAT (5(2X, F10.6))
WRITE (6, 610) ( (B(I,J), J=JMIN,JMAX), I=IMIN,IMAX)
610 FORMAT (7 (1X, F9.5))
END
Dit programma schrijft het volgende naar het scherm:
31.000000 32.000000 33.000000 34.000000 35.000000
36.000000 37.000000 38.000000 39.000000 40.000000
41.000000
-8.00000 -7.00000 -6.00000 -5.00000 -4.00000 -3.00000 -2.00000
-7.00000 -6.00000 -5.00000 -4.00000 -3.00000 -2.00000 -1.00000
-6.00000 -5.00000 -4.00000 -3.00000 -2.00000 -1.00000 0.00000
-5.00000 -4.00000 -3.00000 -2.00000 -1.00000 0.00000 1.00000
-4.00000 -3.00000 -2.00000 -1.00000 0.00000 1.00000 2.00000
-3.00000 -2.00000 -1.00000 0.00000 1.00000 2.00000 3.00000
Verklaring:
De eerste elf getallen zijn een stuk van array A. Het format
specificeert lengte F10.6 voor elk getal. Omdat de elementen
slechts twee cijfers voor de komma hebben, wordt ieder
element uit A links aangevuld met één spatie. Verder worden
tussen de getallen twee spaties gespecificeerd met 2X.
Dit wordt vijf maal gedaan, en dan is het het format aan zijn einde.
Als dit gebeurt wordt een nieuw record begonnen (in dit geval
een nieuwe regel) en begint het format weer aan de groep
specificatie 5(2X,F10.6). Probeer zelf de uitvoer van B te begrijpen.
OPGAVEN:
N \choose M} = N! \over (N-M)!M!
Vul eerst de hele array DLFAC, inclusief het element
DLFAC(0)= 0., met dezelfde iteratie vergelijking als in de vorige
opgave, maar geef nu DLFAC de grenzen 0:100. Lees dan $N$
en M in. Check of N .LT. M, of N .GT. 100 en of M
.LT. 0. Als dit het geval is schrijf een foutenboodschap en ga weer
naar READ (gebruik IF ... THEN ... ENDIF). Als de input
in orde is bereken dan de logarithme van de binomiaal coëfficiënt,
neem de exponent (function EXP) en schrijf het resultaat naar het
scherm. Zet het programma in een oneindige lees loop. Schrijf de
waarden
6 \choose 0, 6 \choose 3, 6 \choose 6
ter controle en schrijf dan
100 \choose 5, 100 \choose 50 en 100 \choose 95
over van het scherm op papier. Merk op dat theoretisch
100 \choose 5 = 100 \choose 95. Is dit zo?
123456789012345678901234567890123456789012345678901234567890123456789012345
0 1.0000
1 1.0000 1.0000
2 1.0000 2.0000 1.0000
3 1.0000 3.0000 3.0000 1.0000
4 1.0000 4.0000 6.0000 4.0000 1.0000
5 1.0000 5.0000 10.0000 10.0000 5.0000 1.0000
6 1.0000 6.0000 15.0000 20.0000 15.0000 6.0000
1.0000
7 1.0000 7.0000 21.0000 35.0000 35.0000 21.0000
7.0000 1.0000
8 1.0000 8.0000 28.0000 56.0000 70.0000 56.0000
28.0000 8.0000 1.0000
9 1.0000 9.0000 36.0000 84.0000 126.0000 126.0000
84.0000 36.0000 9.0000 1.0000
10 1.0000 10.0000 45.0000 120.0000 210.0000 252.0000
210.0000 120.0000 45.0000 10.0000 1.0000