7. Avancerad in- och utmatning (FORMAT)

7.0 Inledning

En mycket använd egenskap hos Fortran är att man kan föreskriva väldigt väl hur utskriften skall se ut vad gäller nya rader och antalet blanka mellan de olika posterna. Det är dock inte så lätt att skriva programmet så att det gör vad man vill, ofta krävs litet av trial and error.

Alla variabler är lagrade i datorn med 1 och 0, men representerar heltal, flyttal, logiska variabler eller bokstäver och andra skrivtecken.

Vid in/utmatning måste därför information finnas om vilken variabeltyp det är frågan om!

  1. Vid liststyrd in/utmatning avgör typerna i listan detta, perfekt för inmatning från terminal (särskilt från skärm). Det är denna typ som vi hitintills har utnyttjat.
  2. Vid format-styrd in/utmatning får programmeraren full kontroll! Ger mycket snygg utmatning. Oftast besvärlig vid inmatning från skärm. Format-styrd inmatning är dock bekväm vid CHARACTER, vid liststyrd inmatning måste nämligen textsträngen omges med apostrofer, det slipper man vid format-styrd inmatning.
  3. Vid oformatterad in/utmatning används bit-mönstret direkt. Perfekt för mellanlagring, tar litet utrymme och litet CPU-resurser. Ger för människan oläslig utskrift.
Notera dock att det använda bit-mönstret vid oformatterad in/utmatning kan skilja sig mellan olika datorfabrikat, se respektive systeminformation, som oftast även innehåller rutiner för omvandling mellan olika system. Även det använda programspråket kan påverka hur lagringen sker, Fortran och C använder olika representationer.

7.1 Formatstyrd utmatning

Formatstyrd utmatning innebär att användaren explicit ger ett format för varje variabel. Det är mycket jobb och ganska bökigt att utnyttja FORMAT, men det ger stora möjligheter. Numera utnyttjas FORMAT nästan bara för utmatning, för inmatning från terminal är liststyrd inmatning perfekt, medan formatstyrd var perfekt vid inmatning med hålkort.

Följande värden på flyttalet A, dubbelprecisionstalet D, komplexa talet C, heltalet I, logiska variabeln L samt tecknet X,

  A = 1.0        D = 2.D0       C = (3.0 , 4.0)  
  I = 25         L = .TRUE.     X = 'X'
ger med utskriftskommandot
      WRITE(*,30) A, D, C, I, L, X 
   30 FORMAT('1 A = ',F8.3,' D = ',F20.16,/
     ,'  C = ',2F8.3,' I = ',I3,' L = ',L1,' X = ',A)
först ny sida och sedan
 A =    1.000 D =   2.0000000000000000 
 C =    3.000   4.000 I =  25 L = T X = X
Här är en förklaring på sin plats. Utskriften av talen i listan sker utnyttjande formatet med nummer 30, vilket dels innehåller information om vilken text som skall skrivas mellan de olika talen, dels hur de olika talen skall skrivas ut. Texten mellan de olika talen, samt eventuellt inledande och avslutande text, skrivs direkt i formatet utnyttjande vanliga apostrofer '. Hur de olika talen skall skrivas ut anges med formatbokstäver. Dessa bokstäver finns listade i Appendix 2.

I ovanstående exempel skrivs det första talet A ut med formatet F8.3, vilket betyder ett flyttal med totalt åtta positioner, varav tre för decimalerna. Notera att utrymme behövs för inledande blank, tecken, heltalssiffra samt decimalkomma, varför F7.3 är tänkbart, men ej F5.3. Det andra talet D skrives ut med formatet F20.16, vilket betyder ett flyttal med totalt 20 positioner, varav 16 för decimalerna. Notera att utrymme även här behövs för inledande blank, tecken, heltalssiffra samt decimalkomma. Det komplexa talet C skrivs ut som två tal, realdelen och imaginärdelen.

Heltalsutmatning betecknas med I och antalet tecken i fältet. Motsvarande gäller logiska variabler och textsträngsvariabler.

En mindre variant på samma utmatning är

      WRITE(*,40) A, D, C, I, L, X  
   40 FORMAT('1 A = ',E18.3,' D = ',D28.16,/
     ,'  C = ',2E18.3,' I = ',I1,' L = ',L1,' X = ',A4)
med resultatet
 A =          0.100E+01 D =             0.2000000000000000D+01
 C =          0.300E+01        0.400E+01 I =  * L = T X =    X
Här är utrymmet för litet för heltalet 25, vilket därför markeras med en stjärna. Notera vidare att vi nu använder formatbokstäverna E och D för att beteckna flyttal med exponent. Fortfarande så anger den sista siffran antalet decimaler, men nu måste vi notera att exponentdelen tar fyra positioner. Både E och D betyder 10 upphöjt till, D är bara en indikation på att dubbel precision använts.

Man kan byta ut E som markering för utmatning på exponentform mot ES, och får då en vetenskaplig (Scientific) form, med utmatning av en siffra skild från noll före decimaltecknet. Om man i stället byter ut E mot EN får man en teknisk (ENgineering) form med en till tre siffror före decimaltecknet, och en exponent jämnt delbar med tre. Om det utmatade värdet är noll erhålles samma utmatning från ES och EN som från E.

Vid användning av vanlig formatstyrd in/utmatning kan man under vissa operativsystem, förutom att ange en fullständig specifikation, alternativt ge bara respektive FORMAT-bokstav. Betydelsen, dvs antalet positioner i fältet, är då systemberoende.

7.2 Styrtecken

Första tecknet i varje post (rad) vid format-styrd utmatning ger ett kontrolltecken till radskrivaren, ibland kallade ASA-tecken efter en tidigare benämning på den amerikanska standardiseringskommissionen ANSI. Dessa har följande betydelse:
        blank     Ny rad
        +         Ej ny rad (överskrivning, 
                  finns ej på alla skrivare)
        0         Dubbel radframmatning
        1         Ny sida
På en del skrivare används ytterligare tecken, siffrorna är ofta vertikala tabulatorer.

OBS: Utmatning med FORMAT(I5) eller F7.3 eller 1PE8.2 kan medföra utmatning av en förfärlig massa papper med nästan inget tryck på, dvs "KARTONGBYTE". Ge därför alltid en explicit blank först i alla FORMAT avsedda för utmatning (om ej annat styrtecken önskas). Erhålles med 1X eller ' ' eller 1H .

Använd därför aldrig samma FORMAT för både in- och utmatning.

Om man vid VAX/VMS först önskar utmatning på fil och sedan på papper bör filtypen .DAT användas, det är bara då som styrtecknen användes automatiskt.

Under UNIX är det litet bökigt att att utnyttja ovanstående styrtecken, det sker på olika sätt hos olika leverantörer.

På Digital Equipment DEC ULTRIX gäller under Fortran 77

  1. Man ger väljaren -vms vid kompileringen
        f77     -vms    prog.f
    
    och får då styrtecknen bortrensade från utmatningen på skärmen.

  2. Man matar ut på en fil som öppnats på ett speciellt sätt
        OPEN(1, FILE = 'utfil', CARRIAGECONTROL = 'FORTRAN')
    
    och får då en fil som utskriven med laserskrivaren ger ny sida och andra egenskaper hos styrtecknen på rätt sätt. Kommandot CARRIAGECONTROL är dock ej standard, varför kompileringsfel erhålles under andra system.

På Digital Equipment UNIX (Alpha) gäller under Fortran 90

  1. Man ger väljaren -vms vid kompileringen
        f90     -vms    prog.f90
    
    och får då styrtecknen rätt hanterade vid utmatning på skärmen.

  2. Man matar ut på en fil som öppnats på ett speciellt sätt
        OPEN(1, FILE = 'utfil', CARRIAGECONTROL = 'FORTRAN')
    
    och får då en fil som utskriven med laserskrivaren ger ny sida och andra egenskaper hos styrtecknen på rätt sätt. Kommandot CARRIAGECONTROL är dock ej standard, varför kompileringsfel erhålles under andra system.

På de flesta system gäller under både Fortran 77 och 90

7.3 Liststyrd utmatning

Den liststyrda utmatningen är mycket bra vid utmatning av resultaten från enkla beräkningar, där layouten inte är så viktig. Man skriver bara
        WRITE(*,*) utdata
där utdata dels är de variabler som skall matas ut, men även kan vara kompletterande text inom apostrofer eller citat-tecken. Se även en något utförligare diskussion i avsnitt 3.7 Enkel in- och utmatning samt nedanstående avsnitt om liststyrd inmatning.

Användning av Hollerith-konstruktionen vid liststyrd utmatning skall undvikas, den fungerar dock under en del Fortran 77 system.

        WRITE(*,*) 32H Mycket olämpligt utmatningssätt
Följande tre utmatningssätt är tillåtna, men det första av dem är både jobbigt och föråldrat.
        WRITE(*,60)
  60    FORMAT(25H Olämpligt utmatningssätt)

        WRITE(*,70)
  70    FORMAT(' Lämpligt utmatningssätt')

        WRITE(*,*) ' Lämpligt utmatningssätt'

7.4 Oformaterad utmatning

Den oformaterade utmatningen är mycket bra vid utmatning av mellanresultat till en fil, för senare inmatning antingen från samma program, eller från ett annat.

Den innebär att in/utmatning sker i det format som variablerna lagras i datorn, dvs i princip som binära siffror och packade på ett sådant sätt att den lagrade informationen är nästan oläslig på skärm eller papper. Denna lagringsmetod har tre stora fördelar:

Oformaterad lagring rekommenderas för all mellanlagring som avser samma maskintyp. Kommunikation mellan olika maskintyper kräver dock oftast att formaterad lagring används, ibland finns speciella konverteringsrutiner så att oformaterad överföring kan utnyttjas.

För att skriva ut oformaterat ger man först ett kommando för att öppna en fil för oformaterad utmatning, se vidare i nästa kapitel. Vi väljer att låta den oformaterade utmatningen ske på enhetsnummer 1 och på en ny fil med namnet utfil.

        OPEN(1, FILE='utfil', STATUS='NEW', FORM = 'UNFORMATTED')
Själva utmatningen sker med en skrivsats som innehåller enhetsnumret inom parentesen, men inget ytterligare. Listan över de olika variablerna skrives på vanligt sätt.
        WRITE(1) A, B, C, D
När man är färdig med utmatningen bör man stänga filen. Vissa system stänger automatiskt alla filer när programmet avslutas på normalt sätt, men ej alltid vid avbrott på grund av fel.
        CLOSE(1)
Vid en eventuell inläsning av dessa data är det väsentligt att de variabler till vilka de inlästa talen tilldelas är av samma typ (och längd) som de skrivna. Man måste naturligtvis ha enheten öppnad innan man kan läsa från den.
        READ(1) E, F, G, H

7.5 Formaterad utmatning av fält

En mycket viktig egenskap i Fortran är att in- och utmatning av hela fält kan ske mycket enkelt, utan att varje enskilt element behöver specificeras individuellt. I nedanstående exempel kan således hela vektorn A skrivas ut genom att enbart namnet A ges i en ut-lista. Alternativt kan ut-listan specificera de element som skall skrivas ut, antingen genom explicit uppräkning eller med en implicit DO-slinga. Slutligen så kan godtyckliga uttryck ges direkt i skrivsatsen.

Som framgår av nedanstående exempel kan man ange antalet element som skall matas ut med en faktor (konstant) framför aktuellt format.

     INTEGER :: I
     REAL, DIMENSION(10)   :: A

     WRITE(*,20) A       ! Utskrift av alla 10 elementen
20   FORMAT(10F10.3)
     WRITE(*,30) A(1), A(2), A(7) ! Utskrift av 3 element
30   FORMAT(3F10.3)

     WRITE(*,50) (A(I), I = 1,9,2)
50   FORMAT(5F10.3)
     WRITE(*,60) A(1)*A(2)+I, SQRT(A(3))
60   FORMAT(2F10.3)
Konstruktionen (A(I), I = nr1, nr2, nr3) kallas för en implicit DO-slinga, och den fungerar nästan som en vanlig explicit DO-slinga
        DO I = nr1, nr2, nr3
           WRITE(*,50) A(I)
        END DO
men med den skillnaden att den explicita slingan skriver ut ett tal per rad, medan den implicita i detta fall skriver fem tal per rad. Skillnaden beror på att varje värde på styrvariabeln I ger upphov till ett nytt anrop av utskriftsrutinen, varvid formatet användes på nytt från början. Man kan säga att varje anrop av WRITE skriver ut minst en ny rad (post) och att på motsvarande sätt varje anrop av READ läser in minst en rad. Konstruktionen med en implicit DO-slinga kan bara användas i WRITE och READ satser.

En viktig egenskap hos format är att man kan ge "för många" utan att det ställer till någon skada. I fallet ovan kan formaten med nummer 30, 50 och 60 samtliga ersättas med det längsta, nämligen det med nummer 20. Om antalet format-poster är för stort jämfört med antalet utmatningsposter så användes inte de överblivna, om det i stället är för litet så börjar utmatningen om från början (jämför dock nedan) av formatet. Exempel på möjliga formatkonstruktioner med motsvarande ut-lista är följande,

   10F12.3                 A
   4(I5,F10.2)             (I(J),A(J), J = 1,4)
Den senare skriver ett heltal I(1), ett flyttal A(1), ett ytterligare heltal I(2), ett flyttal A(2), osv. Formaten kan kapslas
   4(I5,3F10.2)            (I(J),A(J),B(J),C(J), J = 1, 4)
Om formaten "tar slut" börjar det om från början (men på en ny rad)
   (10I8)                  (I(J), J = 1, 1000)
eller från den vänsterparentes som svarar mot den näst sista högerparentesen (eller dess repetitionsfaktor, om sådan finns)
   (2I5, 3(I2,2(I1,I3)), 2(2F8.2,I2))
                         ^         ^
                         ^         Näst sista högerparentes
                         Repetitionsfaktor till 
                         denna parentes
I detta exempel matas således först 17 heltal ut, dvs (2+3(1+2*2)), därefter följer två uppsättningar med vardera två flyttal och ett heltal, därefter följer ny rad med två uppsättningar med vardera två flyttal och ett heltal, följt av ny rad med två uppsättningar med vardera två flyttal och ett heltal, osv, så länge som utdata räcker till.

Det kan även inträffa att man vill ge "för få" tal att skrivas ut, som i nedanstående exempel.

        WRITE(*,10) A, B, C
  10    FORMAT(' A = ', F6.3, ' B = ', F6.3, ' C = ', F6.3)
Detta ger en trevlig utmatning
        A =  1.123 B = -2.345 C = 3.456
men om man bara har två tal att skriva ut erhålles den trista utskriften
        A =  1.123 B = -2.345 C = 
dvs C = står där i sin ensamhet. Om man i stället sätter in kolon efter varje post i formatet och således använder
  10    FORMAT(' A = ', F6.3,:, ' B = ', F6.3,:, ' C = ', F6.3)
så blir utskriften den önskade
        A =  1.123 B = -2.345  

7.6 Avancerad inmatning

7.6.1 Formatstyrd inmatning

Formatstyrd inmatning innebär att för varje element i in-listan användes ett specificerat format, t ex FORMAT(F10.3, E16.6, I7, L3, A8). Detta innebär att i detta fall det första flyttalet skall finnas i inmatningsradens tio första positioner, det andra i de sexton följande positionerna, heltalet i de därpå följande sju positionerna, det logiska elementet i de följande tre, och slutligen texten i de följande åtta positionerna.

7.6.2 Liststyrd inmatning

Liststyrd in/utmatning innebär att ett standardformat utnyttjas för varje datatyp.
                REAL A
                DOUBLE PRECISION D
                COMPLEX C
                INTEGER I
                LOGICAL L
                CHARACTER*1 X

                READ(5,*) A, D, C, I, L, X
på indata
                1. 2. 3. 4 T 'Z'
ger med
                WRITE(6,*) A, D, C, I, L, X
utskriften
                1.000000, 2.0000000000000000 
                (3.000000, 0.000000E+00), 4, TZ
men samma lässats på indata
                1. 2. (3., 4.) 5 .F. 'Y'
ger utskriften
                1.000000, 2.0000000000000000 
                (3.000000, 4.000000), 5, FY
och på indata
                1. 2. 3. 4. X 'Z'
erhålles
                            ^
?Illegal character in data 
eftersom X inte är ett logiskt värde, och slutligen med
                1. 2. 3. 4. T 'T'
erhålles
                1.000000, 2.0000000000000000 
                (3.000000, 0.000000E+00), 4, TT
Regler för liststyrd inmatning.
  1. Komplexa värden måste ges inom parentes, annars blir det ofta fel, till exempel får realdelen rätt värde, imaginärdelen blir noll och nästa storhet får kanske nästa värde i listan (det som eventuellt var tänkt för imaginärdelen).
  2. Textsträngar (CHARACTER) måste inneslutas inom apostrofer.
  3. Variabelvärden åtskiljes av blank, komma eller postslut (radslut).
  4. Två komma direkt efter varandra innebär att motsvarande variabel överhoppas.
  5. Samma värde kan tilldelas flera variabler i följd om det föregås av en multiplikator, t ex
           15*7.0,     femton flyttals-sjuor
            13*,       tretton överhoppade variabler
    
  6. Datorn väntar på samtliga värden i listan (vagnretur räcker ej för att få slut, i nödfall kan tecknet / tillgripas för att markera slut på inläsningen).
  7. Vid liststyrd inmatning av oktala värden (O-format) skall det oktala värdet föregås av citattecknet ".
Vid liststyrd utmatning (fritt format, FMT = *) gäller vissa standard-längder, i princip verkar det vara så att ett tillräckligt antal positioner utnyttjas. Man får ett komma och en blank mellan varje variabel, utom före en textsträngsvariabel (motsvarande A-format).

7.7 Diverse in- och utmatning

Bra tillägg till READ och WRITE är de speciella utgångarna vid fel eller filslut.
        READ(5,...,ERR=snr1, END=snr2)
        WRITE(5,...,ERR=snr3)
Vid paritetsfel och en del andra fel (implementationsberoende) flyttas exekveringen till sats snr1 respektive sats snr3.

På DEC-20 fungerade ERR även då typen på indata ej stämde med variabelns datatyp, den gav då en möjlighet att hoppa tillbaks för ett nytt försök. Ibland ger även filslut återhopp till snr1 (om END=snr2 ej getts).

Vid filslut gäller att END=snr2 ger att exekveringen flyttas till snr2. Ett filslut erhålles med kommandot Kontroll-z eller Kontroll-d. Bra för att avsluta en inläsning.

Den gamle Fortran-programmeraren är van vid att numrera sina Format-satser. Det ser emellertid inte så snyggt ut i ren Fortran 90, där satsnummer inte längre bör användes annat än i undantagsfall. Redan i Fortran 77 fanns, en dock sällan utnyttjad, möjlighet att i stället för ett numrerat format ha en formatvariabel, av typ CHARACTER, som sättes in direkt i respektive skrivsats.

Nedan visar jag tre olika sätt att ge denna tilldelning. De har alla sina för- och nackdelar.

        PROGRAM FORMAT
        IMPLICIT NONE
        REAL                         :: X
        CHARACTER (LEN=11)           :: FORM1
        CHARACTER (LEN=*), PARAMETER :: FORM2 = "( F12.3,A )"
        FORM1 = "( F12.3,A )"
        x = 12.0
        PRINT FORM1, X,  ' HELLO '
        WRITE (*, FORM2) 2*X,  ' HEJ '
        WRITE (*, "( F12.3,A )") 3*X,  ' HEJSAN '
        END
I PRINT-satsen använder jag en textsträngsvariabel FORM1 med längden 11 som tilldelas sitt värde i en explicit tilldelningssats. Nackdelen med denna metod är främst att man måste manuellt räkna antalet tecken, om man anger ett för litet antal ger NAG:s Fortran 90 kompilator inget kompileringsfel, utan felavbrottet kommer först vid exekveringen.

I den första WRITE-satsen använder jag i stället en textsträngskonstant FORM2. Fördelen är att genom att jag använder PARAMETER ej behöver ge någon explicit längd på konstanten, utan kan ange LEN = *. Nackdelen är att en konstant ej kan ges ett nytt värde.

I den andra WRITE-satsen använder jag i stället direkt en textsträng. Nackdelen är att strängen ej kan återanvändas.

Text (CHARACTER) Innehåll Filer


Senast modifierad: 3 maj 1999
boein@nsc.liu.se