8. Filer

8.0 Inledning

Filer är mycket användbara vid programmering. Förutom att de olika programmen oftast finns på filer är det vanligt med indata på fil och att utdata placeras på en fil, dels som lagring och dels inför utskrift på papper. En ytterligare viktig användning är för mellanlagring av resultat, som kanske inte i sin helhet kan rymmas i tillgängligt primärminne.

Vid utmatning på papper är det naturligtvis viktigt att det hela är läsbart, därför bör formaterad eller liststyrd utmatning användas. Vid mellanlagring bör man i stället se till effektiviteten och därför använda oformaterad utmatning, vilket dels drar litet datorresurser och dels ger en kompakt lagring. Vid behov av att läsa mellanresultat "med ögonen" kan man enkelt skriva ett program som läser den oformaterade filen och skriver den formaterat antingen direkt på skärmen eller på en annan fil för senare utmatning på papper.

8.1 Externa filer

Då man använder en extern fil måste man välja ett nummer (logisk enhet) mellan 0 och 99, men naturligtvis ej 5 eller 6, vilka är de vanliga enhetsnumren för in- respektive utmatning. Man skall under UNIX ej heller använda nummer 0, eftersom detta utnyttjas för standard error, dvs felutmatning.

Anm. Många implementationer klarar högre nummer än 99.

För att kunna använda filer måste de först öppnas och sedan stängas. Flera Fortransystem (till exempel Sun och DEC Fortran 77 samt Cray under både Fortran 77 och 90) öppnar dock vid behov automatiskt filer som svarar mot Fortrans nummer, till exempel en fil fort.7 vid utmatning på logisk enhet 7, samt stänger alla öppna filer vid normalt programslut. Andra system (till exempel NAG Fortran 90 på Sun och DEC) kräver att filen har öppnats explicit innan den kan användas, de ger annars en felutskrift när filen behövs.

Om man skall skapa en fil för vanlig utmatning är det mycket enkelt, efter att ha valt ett enhetsnummer och ett filnamn använder man kommandot OPEN och får tillgång till att skriva på filen. När man är klar bör man stänga filen med kommandot CLOSE.

       NOUT = 20
       OPEN(NOUT,FILE='UTFIL.TXT')
       WRITE(NOUT,*) lista1
       WRITE(NOUT,10) lista2
10     FORMAT(     ) ! Här måste en formatspecifikation finnas.
       CLOSE(NOUT)
Man kan nu läsa filen. Även nu måste man först öppna den med kommandot OPEN och man bör stänga filen med kommandot CLOSE. Man behöver inte använda samma enhetsnummer, men man måste använda samma filnamn.
      NIN = 30
      OPEN(NIN,FILE='UTFIL.TXT')
      READ(NIN,*) lista1
      READ(NIN,10) lista2
10    FORMAT(     ) ! Här måste en formatspecifikation finnas.
                    ! Den måste passa med hur filen skrivits.
      CLOSE(NIN)

8.1.1 Vanliga filer

Inledning

Vanliga filer är rent sekventiella filer, dvs man skriver post efter post, för att sedan "spola tillbaks" och läsa post efter post. Möjligheterna att hoppa över en post, gå tillbaks en post eller att skriva över en post med nya värden är mycket begränsade. Man kan bara backa en post med kommandot BACKSPACE och backa till början av filen med REWIND. Båda dessa kommandon skall följas av respektive enhetsnummer. Man kan hoppa över poster vid inläsning med hjälp av READ satser där man inte tar hand om några inlästa värden.

Vanliga filer kan antingen vara formaterade eller oformaterade, normalt är formaterade.

Kommandot OPEN

När man skall skapa en fil väljer man ett enhetsnummer och ett filnamn, och med kommandot OPEN öppnar man filen. Detta kommando kan ges ett stort antal attribut UNIT, FILE, STATUS, ACCESS, FORM, RECL, BLANK, ERR, IOSTAT, ACTION, POSITION, DELIM och PAD. Exempel på OPEN-sats:
        NOUT = 20
        OPEN(NOUT, FILE='UTFIL.TXT', STATUS='NEW', & 
        ACCESS='DIRECT', FORM='FORMATTED')
När man är klar med användningen av en fil bör man stänga filen med kommandot CLOSE. Detta kommando kan ges ett mindre antal attribut, nämligen UNIT, STATUS, ERR och IOSTAT. Exempel på CLOSE-sats:
        CLOSE(NOUT, STATUS = 'DELETE')

Kommandot INQUIRE för filer och filnamn

Ett ytterligare kommando i detta sammanhang är INQUIRE-satsen, vilken kan användas för att se om en viss fil finns, antingen via filnamnet eller via enhetsnumret.
        INQUIRE(UNIT = nummer, lista)
eller
        INQUIRE(FILE = filnamn, lista)
I lista anges exempelvis EXIST = logisk_variabel, varvid den logiska variabeln får värdet sant om respektive fil finns, annars får den värdet falsk. Här följer övriga möjliga parametrar:

Kommandot INQUIRE för list-längd

En ytterligare variant av INQUIRE-satsen kan användas för att bestämma längden av utmatningslistan vid oformaterad utmatning.
        INQUIRE(IOLENGTH = laengd) lista
Här är lista en utmatningslista, till exempel A(1:N). Man får då i laengd reda på längden i systemberoende enheter för motsvarande oformaterade utmatning. Detta kan vara värdefullt i samband med postlängden RECL i direkt-accessfiler.

8.1.2 Direktaccess-filer

Direktaccess-filer är utmärkta då man vill skriva poster i en viss ordning, och ändra vissa poster, och läsa enbart vissa poster. Möjligheterna att hoppa över en post, gå tillbaks en post eller att skriva över en post med nya värden är mycket stora.

Direktaccess-filer användes exempelvis på databaser, som ofta består av ett visst antal fixa poster. Ett bra exempel är en telefonkatalog med ett visst antal tecken för efternamn, förnamn, titel, adress, postnummer, adressort och telefonnummer. Numera är dock de flesta databaser relationella, men om man vill koppla en databas till ett Fortranprogram kan man behöva exportera databasen till en direktaccess-fil i Fortran.

Notera att både vanliga filer (sekventiella) och direktaccess-filer oftast lagras på skivminnen, vilka ofta kallas för direkt access minnen, men då användes direkt access i betydelsen on line.

Direktaccess-filer kan antingen vara formaterade eller oformaterade, normalt är oformaterade.

Vid användning av direktaccess-filer tillkommer några attribut till READ och WRITE-satserna, jämför avsnitt 7.7.

På direktaccess-filer får man inte använda BACKSPACE, ENDFILE eller REWIND. Däremot kan man naturligtvis simulera BACKSPACE med att minska postnumret med ett och REWIND med att sätta det till ett.

Jag använde direktaccess-filer mycket under min tid på Försvarets Forskningsanstalt. Under varje postnummer (REC=nr) lagrade jag rutnätet och alla tillhörande storheter vid en viss tidpunkt för lösningen av en hyperbolisk differentialekvation. Med ett program kunde jag sedan välja ut lämplig tidpunkt (postnummer) och plotta ut önskade storheter vid denna tidpunkt. Med att annat program kunde jag plotta valda storheter som funktion av tiden. Direktaccessfilen kunde även användas för omstart (fortsättning av föregående körning).

Jag ger här ett mycket enkelt exempel på användning av direktaccess-filer, nämligen en databas med telefonnummer över personalen vid Nationellt Superdatorcentrum.

Det första programmet skapar databasen, vilken jag valt vara formaterad och med längden 120 tecken för varje post. Inmatningen av data har jag gjort på enklast möjliga sätt, varefter jag skriver ut den i omvänd ordning. De första 50 positionerna i fältet på direktaccess-filen placeras högerjusterade i X, varför jag vänsterjusterar med ADJUSTL före utmatningen.

      PROGRAM DIREKT_ACCESS
      IMPLICIT NONE
      INTEGER, PARAMETER :: NDIR = 20
      INTEGER            :: NR
      CHARACTER*50 :: X      
      OPEN(NDIR, FILE='direkt.acc', STATUS='UNKNOWN', &
           ACCESS='DIRECT', FORM='FORMATTED', RECL=120)
      WRITE(NDIR,10,REC=1) "Bo Einarsson;013-281432"
      WRITE(NDIR,10,REC=2) "Bo Sjögren;013-282625"
      WRITE(NDIR,10,REC=3) "Mats S Andersson;013-282568"
      WRITE(NDIR,10,REC=4) "Ulla Bjuhr-Jönsson;013-282618"
      WRITE(NDIR,10,REC=5) "Larsgunnar Nilsson;013-281107"
      DO NR = 5, 1, -1
         READ(NDIR,10,REC=NR) X
         WRITE(*,*) ADJUSTL(X)
      END DO  
      CLOSE(NDIR)
      STOP
 10   FORMAT(A50)
      END PROGRAM DIREKT_ACCESS
Det andra programmet använder databasen, man ger önskat postnummer och får ut önskad individ. Notera att man inte behöver läsa in hela databasen, utan bara den post som man är intresserad av.
      PROGRAM LAES_DIREKT_ACCESS
      IMPLICIT NONE
      INTEGER, PARAMETER :: NDIR = 20
      INTEGER            :: NR
      CHARACTER*50 :: X      
      OPEN(NDIR, FILE='direkt.acc', STATUS='OLD', &
         ACCESS='DIRECT', FORM='FORMATTED', RECL=120)
      DO
          WRITE(*,'(A)',ADVANCE='NO') ' Ge nr = '
          READ(*,*) NR
          IF (NR < 1 ) EXIT
          READ(NDIR,10,REC=NR) X
          WRITE(*,*) ADJUSTL(X)
      END DO  
      CLOSE(NDIR)
      STOP
 10   FORMAT(A50)
      END PROGRAM LAES_DIREKT_ACCESS
Begreppet ADVANCE behandlas i avsnitt 16.4.

8.2 Interna filer

Interna filer kan användas i de fall man först vill läsa en post med ett format och sedan läsa den (fortfarande samma post) med ett annat format. Vad man gör då är att skriva på en fil i primärminnet (intern fil) i stället för en fil på sekundärminnet (skivminnet, extern fil).

Ett exempel ges nedan på detta (något krångliga) förfarande, kört både på den gamla DEC-20 under operativsystemet TOPS-20 och på en modern UNIX-maskin. Vi skapar här den interna filen genom att deklarera KORT som en textsträng med 80 tecken. Vanlig inläsning utnyttjande textformat sker till denna interna fil i sats nummer 15 nedan, vanlig utskrift av den sker i sats 22, medan den interna filen återanvändes för inläsning i satserna 28 och 50, utnyttjande heltals respektive flyttalsformat.

      PROGRAM INTERN
C     DETTA PROGRAM DEMONSTRERAR ANVÄNDNINGEN AV INTERNA
C     FILER. NOTERA ATT IBLAND BETRAKTAS BLANKA SOM NOLLOR,
C     DVS PÅ TOPS-20 ANVÄNDES BZ. VID VANLIG 
C     HELTALSINMATNING UNDER TOPS-20 GÄLLDE DÄREMOT BN. 
C     UNDER UNIX GÄLLER BN.
      CHARACTER KORT*80
      INTEGER HELTAL
      REAL FLYTAL
10    WRITE(6,11) 
11    FORMAT(' IN: ')
15    READ(5,20,END=90) KORT
20    FORMAT(A80)
22    WRITE(6,25) KORT
25    FORMAT(' UT: ',A80)
28    READ(KORT,30,ERR=50) HELTAL
30    FORMAT(I5)
      WRITE(6,40) HELTAL
40    FORMAT(' HELTAL = ',I5)
      GOTO 10
50    READ(KORT,60,ERR=80) FLYTAL
60    FORMAT(F20.5)
      WRITE(6,70) FLYTAL
70    FORMAT(' FLYTTAL = ',F15.8)
      GOTO 10
80    CONTINUE
      GOTO 10
90    STOP
      END
Programmet ovan läser in en post (rad, hålkort med 80 tecken) och skriver ut den oförändrad. Därefter kollar programmet om det står ett heltal i de första fem positionerna, i så fall skrivs heltalet ut och programmet begär ny post. Om programmet ej funnit heltal kollar det i stället om det är ett flyttal i de första 20 positionerna, skriver ut det och begär nästa post. Man slutar genom att ge sluttecknet Kontroll-z.

Ovanstående princip kan användas om man inte vet exakt vilken typ av data som kommer.

Körning av detta program gav på DEC-20 under TOPS-20 respektive på Sun SPARC under Sun OS 4.1.2.

DEC                             Sun (vid skillnad)
@EX INTERN                      f77 intern.f
FORTRAN: INTERN                 intern.f
INTERN                          MAIN intern:
LINK:     Loading               a.out
LNKXCT    INTERN execution
IN: 
ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖÉÜ
UT: ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖÉÜ

IN: 
12345
UT: 12345                              
HELTAL = 12345
IN: 
1
UT: 1                                  
HELTAL = 10000                  HELTAL =     1
IN: 
1 1 1
UT: 1 1 1                              
HELTAL = 10101                  HELTAL =   111
IN: 
12.56
UT: 12.56                              
FLYTTAL =     12.55999994       FLYTTAL =     12.56000042
IN: 
9.1
UT: 9.1    
FLYTTAL =      9.10000002       FLYTTAL =      9.10000038
IN:   
  9.1
UT:   9.1    
FLYTTAL =      9.10000002       FLYTTAL =      9.10000038
IN: 
kontroll-Z
                                Suspended

Övning:

Kör detta program på ditt system och se vad som händer!

Avancerad in- och utmatning (FORMAT) Innehåll Formen hos ett Fortran-program


Senast modifierad: 29 maj 2002
boein@nsc.liu.se