8. Filer
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.
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)
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.
- UNIT anger vilket enhetsnummer som skall användas. Kan ges som enbart talet (och då
först bland attributen) eller med UNIT=talet.
- FILE anger filens namn. Kan ges som FILE=VAR, där textsträngsvariabeln VAR innehåller
filnamnet, eller enklare som FILE='filnamn'.
- STATUS kan vara 'OLD', 'NEW', 'SCRATCH', 'REPLACE'
eller 'UNKNOWN', vilket anger att det är en gammal fil,
en ny fil, en tillfällig fil, eller att typen är okänd, dvs
oftast att det är en ny eller gammal fil.
Attributet 'REPLACE' innebär att om en fil med angivet
namn ej finns skapas en sådan och ges attributet 'OLD'.
Attributet 'REPLACE' innebär att om en fil med angivet
namn finns så tas denna bort och en ny med angivet namn skapas
och ges attributet 'OLD'. Fallet 'UNKNOWN' är
installationsberoende, men normalt har det samma effekt som
'REPLACE'.
Om något attribut ej ges blir det automatiskt 'UNKNOWN'.
- ACCESS = 'SEQUENTIAL' anger att det är fråga om en vanlig sekventiell fil och ACCESS
= 'DIRECT' anger att det är en direktaccess-fil, jfr sektion 8.1.2. Om attributet ej ges
blir det automatiskt en sekventiell fil.
- FORM = 'FORMATTED' anger att det är fråga om en formaterad fil medan däremot
FORM = 'UNFORMATTED' anger att det är en oformaterad fil. Om attributet ej ges blir det automatiskt
en formaterad fil vid sekventiell fil, men en oformaterad fil vid direktaccess-fil. Observera att
list-styrda filer i detta sammanhang betraktas som formaterade.
- RECL=längd anger postlängden i direktaccess-filer, se
sektion 8.1.2.
- BLANK = 'ZERO' anger att blanktecken i tal som läses skall tolkas som nollor medan däremot
BLANK = 'NULL' anger att blanktecken i tal som läses skall nonchaleras, enbart blanktecken
tolkas dock som noll. Om attributet ej getts användes skönsvärdet
BLANK = 'NULL'.
- ERR=satsnr anger var exekveringen skall fortsätta vid fel vid utförande av OPEN-satsen.
- IOSTAT=variabel får ett positivt värde om fel inträffar vid utförande
av OPEN-satsen,
och ett negativt värde om enbart ett filslut påträffas, och ett annat negativt
värde om enbart ett postslut påträffas. Variabeln blir naturligtvis noll om inget
fel inträffar.
- ACTION = 'READ' anger att filen endast får läsas, 'WRITE' att den ej får läsas,
medan däremot 'READWRITE' anger att båda operationerna är tillåtna.
- POSITION = 'ASIS' anger att filen skall användas i sin nuvarande position, 'REWIND' att den
skall användas från början, medan däremot 'APPEND' anger att den skall
användas från slutet (t ex skriva in mer information på slutet).
- DELIM = 'APOSTROPHE' anger att apostrofer ' användes för att avgränsa textsträngar
vid list-styrd formatering eller vid utnyttjande av NAMELIST. Om däremot 'QUOTE' angetts
betyder det att citat-tecken " användes, i båda fallen dubbelskrives eventuellt tecken
i textsträngen av respektive slag. Däremot anger standardfallet 'NONE' att ingen avgränsning
görs. Detta attribut kan endast användas vid formaterade filer, och nonchaleras vid
normal formaterad inmatning.
- PAD = 'YES' anger att om vid formaterad inmatning en post innehåller för få element
kompletteras med blanka, om däremot 'NO' angetts betyder det att antalet element i inmatningslista
och format-specifikationen måste överensstämma. Detta attribut kan endast användas
vid formaterade filer och formaterad in- och utmatning. Om attributet ej getts gäller 'YES'.
OBS: För Fortran 77 gällde i princip däremot PAD = 'NO'.
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.
- STATUS kan här vara 'KEEP' eller 'DELETE', vilket anger att filen skall sparas eller tas
bort vid stängningen. En fil som öppnats som 'SCRATCH' kan ej sparas.
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:
- ERR=satsnr anger var exekveringen skall fortsätta vid fel
vid utförande av INQUIRE-satsen.
- IOSTAT=variabel får ett positivt värde om fel
inträffar vid utförande av INQUIRE-satsen,
och ett negativt värde om enbart ett filslut påträffas,
och ett annat negativt värde om enbart ett postslut påträffas.
Variabeln blir naturligtvis noll om inget
fel inträffar.
- OPENED = logisk_variabel, varvid
den logiska variabeln får värdet sant om respektive fil eller
enhet är öppnad, annars får den värdet falsk.
- NUMBER = heltals_variabel, varvid
heltalsvariabeln får värdet av enhetsnumret, eller
-1 om ingen enhet är kopplad till filnamnet.
- NAME = textsträngs_variabel, varvid
variabeln tilldelas filnamnet, eventuellt kvalificerat (utvidgat).
Det kan användas i en följande OPEN-sats.
- NAMED = logisk_variabel, varvid
den logiska variabeln får värdet sant om respektive fil
har ett namn, annars får den värdet falsk.
- ACCESS = textsträngs_variabel, varvid variabeln tilldelas
något av värdena 'SEQUENTIAL', 'DIRECT' eller
'UNDEFINED', det senare om enheten ej är kopplad.
- SEQUENTIAL = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES', 'NO' eller 'UNKNOWN' beroende
på om filen kan öppnas för sekventiell access.
- DIRECT = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES', 'NO' eller 'UNKNOWN' beroende
på om filen kan öppnas för direkt-access.
- FORM = textsträngs_variabel, varvid variabeln tilldelas
värdet 'FORMATTED', 'UNFORMATTED' eller 'UNDEFINED' beroende
på vilket slags fil som är kopplad, sista alternativet
om ingen fil är kopplad.
- FORMATTED = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES', 'NO' eller 'UNKNOWN' beroende
på om filen kan öppnas för formaterad access.
- UNFORMATTED = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES', 'NO' eller 'UNKNOWN' beroende
på om filen kan öppnas för oformaterad access.
- RECL = heltals_variabel, varvid
heltalsvariabeln får värdet av maximala postlängden
tillåten för filen. Längden är antalet
tecken vid enbart formaterad teststräng, och systemberoende i
alla andra fall.
- NEXTREC = heltals_variabel, varvid
heltalsvariabeln får värdet av senaste lästa eller skrivna
posten plus 1.
- BLANK = textsträngs_variabel, varvid variabeln tilldelas
värdet 'NULL', 'ZERO' eller 'UNKNOWN' beroende
på om blanka tecken i numeriska fält skall nonchaleras, tolkas
som noll, eller om det antingen saknas koppling eller om kopplingen
inte är för formaterad in/ut-matning.
- POSITION = textsträngs_variabel, varvid variabeln tilldelas
värdet 'REWIND', 'APPEND', 'ASIS'
eller 'UNDEFINED' beroende
på aktuell position (i första hand från motsvarande
OPEN-sats). Om det saknas koppling, eller om kopplingen avser
direkt-access, gäller fallet 'UNDEFINED'.
- ACTION = textsträngs_variabel, varvid variabeln tilldelas
värdet 'READ', 'WRITE', 'READWRITE'
eller 'UNDEFINED' beroende
på aktuellt fall (i första hand från motsvarande
OPEN-sats). Om det saknas koppling gäller fallet 'UNDEFINED'.
- READ = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES', 'NO' eller 'UNKNOWN' beroende
på om läsning är tillåten.
- WRITE = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES', 'NO' eller 'UNKNOWN' beroende
på om skrivning är tillåten.
- READWRITE = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES', 'NO' eller 'UNKNOWN' beroende
på om både läsning och skrivning är tillåten.
- DELIM = textsträngs_variabel, varvid variabeln tilldelas
värdet 'APOSTROPHE', 'QUOTE' eller 'NONE'
efter vad som specificerats i motsvarande
OPEN-sats. Om det saknas koppling, eller
om den inte avser formaterad in/ut-matning, gäller i stället
'UNDEFINED'.
- PAD = textsträngs_variabel, varvid variabeln tilldelas
värdet 'YES' eller 'NO' efter vad som specificerats
i motsvarande
OPEN-sats.
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.
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.
- UNIT anger som vanligt vilket enhetsnummer som skall användas. Kan ges som enbart talet (och
då först bland attributen) eller med UNIT=talet.
- FMT = nummer eller format får vara med, om det saknas är överföringen oformaterad.
Om det är andra element kan det ges utan FMT = (om även
enhetsnummret getts utan UNIT =). Vid direktaccess får FMT = *, dvs liststyrt format, ej användas.
- END får ej ingå. Direktaccess-filer har nämligen
inget formellt filslut.
- ERR får ingå. De fel som avkännes är
implementationsberoende.
- REC = nummer måste vara med, innehåller numret på
den post man vill läsa eller skriva, kallas ibland "postnummer".
- IOSTAT = HELTALSVARIABEL. Denna variabel är noll om inget fel
inträffat, positiv vid fel, och negativ vid filslut.
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.
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!
Senast modifierad: 29 maj 2002
boein@nsc.liu.se