Innhold:
Inn- og utoperasjoner
I programmering brukes begrepet inn/utoperasjoner (engelsk: Input/Output), I/O, eller bare IO når programmet leser eller skriver innholdet av variable, også bare kalt data, til omverdenen. Vanligvis er omverdenen utskrift av data til skjermen eller innlesing av data fra verdier hamret inn på tastaturet.
Innlesing fra tastatur og utskrift til skjerm er greit for å teste enkle ting i programmering, men upraktisk hvis datamengdene blir særlig større enn en skjermfull eller resultatene skal lagres for senere bruk. Mengden av data fra beregninger av været, enkle skipsmodeller eller andre konstruksjoner kan fort komme opp i milliarder av bytes (gigabytes). Data fra seismiske undersøkelser er i størrelsesorden petabytes (en million milliarder bytes). Større datamengder må lagres på fil. I tillegg til lesing fra tastatur og skriving til skjerm må derfor programmeringsspråk som Matlab kunne lesing og skriving til fil, også kalt fil-IO (engelsk: File IO).
Lagring av variable til fil
Ved normal avslutning av Matlab eller Octave forsvinner alle data lagret i variable. Det fins to funksjoner for å lagre unna og laste inn variable fra en sesjon til fil, save
og load
. Variablene lagres i spesielle Matlab-filer, og kan som regel ikke leses av andre programmer enn Matlab. I tillegg kan det være problemer med å lese en fil skrevet i en nyere versjon av Matlab-programmet med en eldre Matlab. Det er heller ikke problemfritt å utveksle filer mellom Matlab og Octave.
Siden save
og load
ikke er egnet til å lagre data til bruk for andre program enn Matlab brukes de hovedsaklig til å lagre unna variable i arbeidsområdet for bruk på et senere tidspunkt. En tabell A
kan lagres på fil med:
A = [1 2 3; 4 5 6]; save('-ascii'1, 'minfil.txt', 'A');
Navnet på variabelen som skal lagres må skrives inn som en tekststreng, 'A'
i dette tilfellet. Filenminfil.txt
er en vanlig tekstfil og kan sjekkes ut med Notepad eller en hvilken som helst annen editor. Tabellen kan leses inn igjen til arbeidsområdet med load
,
A = load('-ascii', 'minfil.txt');
De innebygde funksjonenesave
ogload
kan brukes på mange forskjellige måter. Her er det bare vist en enkel måte å bruke dem på som bør virke mellom de aller fleste Matlab- og Octave-versjoner.
I eksempelet på bruk avload
kan'-ascii'
sløyfes selv om det ikke er anbefalt.
1ASCII er et historisk argument. Det betyr American Standard Code for Information Interchange, og er den første standarden for koding av tegnsett på datamaskin. Første utgave av standarden ble utgitt i 1963. De første 128 tegnene (kode 0-127) av dagens standard, UTF-8, er identisk med ASCII.
Eksempel
Filen 'befolkning.txt'
inneholder informasjon om befolkningsutviklingen i Norge på begynnelsen av 2000-tallet, fordelt på antall innbyggere i byen og på landet. For å holde oversikten over hvilke data som er lagret i hver kolonne er det lagt til informasjon om dette i en Matlab kommentarsetning på første linje:
% År Tettbygd Spredtbygd Uplassert 2000 3396382 998922 83193 2001 3419975 1025055 58406 2002 3474623 1022609 26834 2003 3514417 1014854 22981 2004 3536454 1020840 20163 2005 3560137 1027690 18536 2006 3607813 1016736 15670 2007 3722786 1000900 13485 2008 3780068 1009400 9766 |
Informasjonen kan leses inn i en Matlab-variabel (tabell) med
B = load('-ascii', 'befolkning.txt');
Befolkningen i ett bestemt år, f.eks. 2005, kan regnes ut med setningene
aar = 2005; antall_innbyggere = sum(B(aar-1999,2:4));
Uttrykket aar-1999
gir hvilken rad i tabellen ett bestemt år tilsvarer. Den innebygde funksjonensum
brukes til å legge sammen befolkningsgruppene for et bestemt år, lagret i kolonne 2, 3 og 4.
Tekst- og binærfiler
Desimaltall kan lagres til fil på to måter, som tekst eller binært. Ved lagring av tall som tekst oversettes tallet til en tekststreng og lagres i en vanlig tekstfil, for eksempel med num2str
:
>> format long >> x = 1 + 1/1024 x = 1.00097656250000 >> string = num2str(x,10) string = 1.000976562
I eksempelet gjøres 1+1/1024
om til en 10-siffers tekststreng, '1.000976562'. Det er lett å sjekke innholdet av tekstfiler, men det er ikke bra å lagre store datamengder som tekst, fordi
- å lagre tall som tekst tar mer plass
- omgjøring fra tall til tekst og tilbake krever ekstra beregninger
Å lagre tegn som 'a'
, 'b'
, 'c'
, '1'
, '2'
osv. krever minst 1 byte lagerplass pr. tegn (1 i Octave, 2 i Matlab). Lagring av ett tall binært ved å overføre innholdet av dataminnet (RAM) direkte til fil krever 8 tegn lagerplass:
>> whos *** local user variables: Prot Name Size Bytes Class ==== ==== ==== ===== ===== rwd string 1x11 11 char rwd x 1x1 8 double Total is 12 elements using 19 bytes
En studie av hva som lagres i minnet viser at oversetting av tallverdien 1+1/1024
til tekst med 10 sifre, lagring til fil og tilbakelesing til en variabel tar mer plass enn de opprinnelige dataene, og klarer heller ikke å gjenskape den opprinnelige verdien.
Overføring av et datum til og fra tekstfil
Datum | Eksakt innhold | Heksadesimalt innhold i minnet | Plass på fil i bytes |
---|---|---|---|
Opprinnelig verdi | 1 + 1/1024 | 3ff00400000000002 | 8 |
Verdi som streng | '1.000976562' | 312e303030393736353632 | 11 |
Tilbakelest verdi | 1 + 1/1024 - 5×10-10 | 3ff003ffffdda3e8 | 8 |
2 Et desimaltall lagres som (-1)s × mantisse × 2eksponent-1023. Den første biten i minnet er fortegnet s, deretter 11 biter for eksponent og 53 biter for mantisse. Første bit i mantissen er alltid 1 for tall forskjellig fra 0, og lagres ikke.
Åpning og lukking av filer
For å overføre innholdet av en variabel direkte fra minnet i Matlab til fil må filen åpnes for binær overføring. Det er nødvendig å lage en kobling mellom Matlab-programmet og filen som skal leses eller skrives. Den innebygde funksjonen fopen
gjør det:
[filnummer beskjed] = fopen('filnavn', 'tilgangstype');
Variabelen filnummer
blir satt til et tall > 0 hvis filåpningen gikk bra, ellers får den verdien -1. Ved feil lagres det en tekststreng i beskjed
som forklarer hvorfor det gikk galt. Tekststrengen'filnavn'
angir et stinavn til filen som skal åpnes. Den siste parameteren, strengen'tilgangstype'
er en kode for hva slags filoverføring som skal finne sted:
Streng | Filoperasjon |
---|---|
'r' | Åpne en fil for lesing |
'w' | Åpne en fil for skriving og fjern eventuelt gammelt innhold. Lag en ny fil hvis den ikke fins. |
'a' | Åpne en fil for å legge til nye data på slutten (logging). Lag en ny fil hvis den ikke fins. |
'r+' | Åpne en fil som fins fra før for lesing og skriving. |
'w+' | Åpne en fil for lesing og skriving, og fjern eventuelt gammelt innhold. Lag en ny fil hvis den ikke fins. |
'a+' | Åpne en fil for å lese og legge til nye data på slutten (logging). Lag en ny fil hvis den ikke fins. |
Ved oppstart av Matlab eller Octave åpnes 3 filer automatisk, med filnumre 0,1 og 2:
Når det ikke er mer som skal gjøres bør forbindelsen til filen lukkes med fclose
:
status = fclose(filnummer);
Hvis filen kan lukkes uten problemer får status
verdien 0, ellers -1. Filnumrene 0, 1 og 2 kan også stenges av med fclose
. Ved lukking av en fil vil operativsystemet sende eventuelle fildata som er mellomlagret i minnet ut til disken.
Åpning og lukking av filer regnes som kostbare operasjoner i programmering. Selv om moderne operativsystemer takler et stort antall samtidig åpne filer lønner det seg å holde antallet så lavt som mulig av hensyn til ressursbruken, spesielt bruk av minne for mellomlagring av filinnhold. Tilsvarende lønner det seg å redusere antall åpninger og lukkinger av filer, f.eks. ved å unngå slike operasjoner inne i for-løkker.
Eksempler
>> [fid,msg] = fopen('varmeledning.dat','w'); >> fid fid = 3 >> fclose(3) ans = 0 >> [fid,msg] = fopen('tulll','r') fid = -1 msg = No such file or directory
Det første kallet til fopen
lykkes med å åpne 'varmeledning.dat'
for skriving, mens det siste kallet mislykkes i å åpne 'tulll'
for lesing. Tilbakemeldingen er at det fins ingen fil med dette navnet.
Lesing og skriving av binærfiler
Innholdet i en variabel overføres direkte til fil med fwrite
. Dataene lagres på binær form. Det er normalt ikke mulig å se på innholdet i binære filer med en editor. I de tilfellene editoren klarer å åpne en binærfil vil det bare komme opp en haug med rare tegn, lik ufint språk i et Donald-hefte. Selv om binærfiler ikke er så nyttige for mennesker er de nyttige for maskiner, fordi det er den mest effektive måten å overføre data fra minne til disk.
Hvis filen 'test.dat'
er åpnet for skriving med fopen
og filnummeret ble lagret i filnr
kan tabellen A
overføres direkte til disk med
antall = fwrite(filnr, A, 'double');
Returvariabelen antall
inneholder antall elementer i A
som ble overført til disk. Hvis alt går bra skal den inneholde antall rader × antall kolonner. Strengen 'double'
må være med for å angi atA
skal skrives ut som et desimaltall (et tall med desimalpunkt), og ikke gjøres om til noe annet ved lagring. Det er mulig, men er ikke tema her.
En vanlig 2-dimensjonal tabell ordner elementene i rader og kolonner. Begrepene rader og kolonner har ingen mening i en fil. Der lagres tallene bare i en lang rekke eller liste etter hverandre, i enstrøm av bytes. Når Matlab lagrer en tabell på binær form til fil, skrives elementene ut kolonne for kolonne. Først alle elementene i første kolonne, så alle elementene i andre kolonne, osv. Som i minnet (RAM), nummereres data i filer i bytes. Første element lagres derfor i byte nummer 0-7, andre element i byte 8-15 og tilsvarende. En tabell A
med 2 rader og 3 kolonner vil derfor lagres som i en kommode med 48 nummererte skuffer, fra skuff 0 til og med skuff 47:
A(1,1) | A(2,1) | A(1,2) | A(2,2) | A(1,3) | A(2,3) |
---|---|---|---|---|---|
byte 0-7 | byte 8-15 | byte 16-23 | byte 24-31 | byte 32-39 | byte 40-47 |
Eksempel på skriving til fil
>> A = [1 2 3; 4 5 6]; >> fid = fopen('test.dat', 'w') fid = 3 >> fwrite(fid, A, 'double') ans = 6 >> fclose(fid) ans = 0 >> whos A Name Size Bytes Class Attributes A 2x3 48 double >> ls -l test.dat -rw-rw-r--. 1 ola ola 48 2009-09-01 14:32 test.dat
Kommandoen whos
viser at A
er en tabell med 2 rader og 3 kolonner, og at den bruker 48 bytes lagerplass i minnet. Etter skriving til fil og lukking av filen viser ls
på Linux eller Mac at dataene bruker 48 bytes lagerplass på disk. I Windows viser ikke ls
filstørrelsen, men det er mulig å få ut denne ved å lagre resultatet av dir
i en variabel, f.eks. r = dir('test.dat')
.
Siden data i binærfiler er lagret i en lang rekke, må programmereren bestemme seg for hvordan dataene skal tolkes ved innlesing fra disk. Det enkleste er å lese inn alt i en lang liste med
L = fread(fid, inf, 'double');
Parameteren inf
står for infinity, eller at så mye som mulig skal leses, mens strengen 'double '
er med som i fwrite
for å angi at det er desimaltall som leses. Skal de 6 første desimaltallene inn i en 2×3-tabell A
, blir kallet
A = fread(fid, [2 3], 'double');
Skal dataene lagres i en tabell med 2 rader, mens det er ukjent eller uinteressant hvor mange kolonner binærfilen inneholder, er det mulig å si at alle kolonnene skal leses inn med inf
:
A = fread(fid, [2 inf], 'double');
Det er ikke mulig å angi uendelig rad-dimensjon når data lagres kolonnevis. Skal data leses inn til en tabell med mer enn 2 dimensjoner er det tilsvarende bare den siste dimensjonen som kan settes likinf
. Inneholder filen for få elementer til å fylle ut hele tabellen slenger Matlab på ekstra 0-elementer på slutten som vanlig.
Filposisjonering
Når en fil åpnes for lesing eller skriving holder operativsystemet orden på hvor i filen programmet leser eller skriver til enhver tid. Åpnes en ny fil for skriving settes filposisjonen til 0, og åpnes en fil for å legge til på slutten (med tilgangstype 'a'
eller 'a+'
) settes posisjonen lik størrelsen på filen. Posisjon eller foranding av posisjon i en fil angis i antall bytes, nummerert fra byte 0 og utover.
Et Matlab-program kan lese av filposisjonen med ftell
, og i tillegg endre den med fseek
. Etter at en fil er åpnet og tildelt et filnummer
med fopen
vil kallet
posisjon = ftell(filnummer);
returnere posisjonen i filen hvor neste lesing eller skriving skal finne sted. Klarer ftell
å finne filposisjonen returneres en verdi i området 0 til filens lengde, ved feil returneres -1.
Kalles fread
eller fwrite
flere ganger etter hverandre leses eller skrives filen i rekkefølge, ellersekvensielt, fra begynnelsen til slutten. Det er den vanligste måten å gjøre fil-IO på, men det er mulig å skrive eller lese til en fil i et tilfeldig mønster ved å endre filposisjonen før lesing eller skriving med fseek
:
status = fseek(filnummer, posisjon, utgangspunkt);
Parameteren utgangspunkt
er en streng med 3 lovlige verdier,
Ved å kalle fseek
settes en ny posisjon for neste lesing eller skriving. Går det bra å sette ny posisjon returneres status 0, ellers returneres -1. Kallet
status = fseek(filnummer, 0, 'bof');
flytter neste innlesing eller utskrift tilbake til begynnelsen av filen, mens
status = fseek(filnummer, 0, 'eof');
flytter til slutten av filen. Utgangspunktet 'cof'
brukes til å endre filposisjonen i forhold til siste lesing eller skriving. Som eksempel vil
status = fseek(filnummer, -8, 'cof');
flytte seg 8 bytes tilbake i forhold til nåværende posisjon, eller med andre ord flytte seg tilbake til siste element hvis utgangspunktet var rett etter en nylig innlest eller utskrevet tabell.
I motsetning til andre programmeringsspråk er det ikke mulig å flytte filposisjonen til en plass utenfor filen for å lage filer med hull i Matlab.
Et fullstendig eksempel
En tabell A
med 3 rader og 3 kolonner lagres binært til 'tab33.dat'
. Etterpå leses to lister B
ogC
inn fra filen, med verdier fra A
som angitt i figuren:
Å opprette A
og skrive den ut til 'tab33.dat'
gjøres med setningene:
A = [1:3; 4:6; 7:9]; [fid mld] = fopen('tab33.dat', 'w'); antall_elementer = fwrite(fid, A, 'double'); status = fclose(fid);
Setningene lager en tabell, åpner filen 'tab33.dat' for skriving, skriver ut A
på binær form og lukker filforbindelsen etter lagring. Sjekk av returverdier er utelatt for å få koden så kort som mulig, men det bør være med i et ferdig program. Hvis setningene kjøres uten feil blir det opprettet en fil'tab33.dat'
på 9*8 = 72 bytes. Tabellene (listene) B
og C
kan nå leses inn ved å åpne'tab33.dat'
for lesing og så posisjonere seg riktig i filen med fseek
før innlesing:
[fid mld] = fopen('tab33.dat', 'r'); status = fseek(fid, 8*1, 'bof'); B = fread(fid, 2, 'double'); status = fseek(fid, 8*4, 'cof'); C = fread(fid, [2 1], 'double');
Funksjonen fseek
skal ha inn avstand i antall bytes, så derfor må avstand i antall elementer skrives som 8*n for n elementer. Etter første innlesing er filposisjonen 24, så den må flyttes 4 elementer til begynnelsen av C
. Både B og C er nøyaktig like store, men for å få riktig antall rader og kolonner på tabellen er det bedre å bruke [2 1]
, enn bare å angi antall elementer som ved lesing av B
. Sjekk av returverdier er ikke vist.
Formatert utskrift
Funksjonen disp
brukes ofte til utskrift fra skriptfiler eller m-filer i Matlab. Blandet utskrift av tekst og tall er imidlertid tungvint fordi tallene må gjøres om til tekst med num2str
. Funksjonenfprintf
3 kan brukes til mer presis kontroll av utskrift. Den er beregnet på utskrift til tekstfiler åpnet med fopen
, men kan også brukes for utskrift til skjerm fordi skjermen alltid har filnummer 1. I stedet for å gjøre om alt til tekst bruker fprintf
et ekstra argument, en format-streng, for å beskrive hvordan ting skal se ut. Format-strengen kan inneholde en blanding av forklarende tekst og utskriftskoder som forteller hvordan variablene som følger skal skrives ut, for eksempel
>> fprintf(1,'Pi med 10 desimaler: %.10f\n', pi); Pi med 10 desimaler: 3.1415926536 >> fprintf(1,'Pi med 2 desimaler over 5 tegns bredde >%5.2f<\n', pi); Pi med 2 desimaler over 5 tegns bredde > 3.14<
Et utskriftsformat er på formen
%<bredde><.antall desimaler><typebokstav>
Det begynner alltid med % (prosent), har en valgfri total bredde, et valgfritt antall desimaler og avsluttes med en typebokstav som sier hva slags data som skal skrives ut. Typebokstavene angir at argumentet skal skrives ut som
Avslutningen med '\n'
betyr at det skal skrives et linjeskift. Ved utskrift av en tabell gjentas format-strengen for alle elementene:
>> A = [25 77 128 255]; >> fprintf(1, 'Heksadesimale verdier:'); fprintf(1,' 0x%x', A); fprintf(1,'\n'); Heksadesimale verdier: 0x19 0x4d 0x80 0xff
Linjeskift legges bare inn i den siste fprintf
-setningen. I et skript er det naturlig å skrive enfprintf
på hver linje. Det er mulig å blande disp
og fprintf
ved utskrift.
3 Octave har en ekstra utskriftsfunksjon printf
. Den fungerer som fprintf
, bortsett fra at den mangler argumentet for filnummer — den skriver alltid ut til skjermen.
Formatert innlesing
Analogt til fread for binær IO, fins det en funksjon fscanf
for å lese inn data lagret i tekstfiler. Den har et format-argument som for fprintf
og et argument for antall elementer som skal leses likfread
. Funksjonen kan kalles på to forskjellige måter:
A = fscanf(filnummer, 'format', antall); [A antall_innlest] = fscanf(filnummer, 'format', antall);
I tillegg til variabelen som skal motta dataene er det mulig å angi en variabel antall_innlest
som fylles inn med antall elementer som ble lest inn i A
. Antall elementer som skal leses inn kan angis med inf
(så mange som mulig), totalt antall elementer, eller antall rader og kolonner [M N]
. Dette fungerer nøyaktig som i fread
.
Eksempel på formatert lesing
Anta filen test.dat
inneholder en linje med ett tall i hver 5. kolonne, inkludert blanke tegn:
1 5 3 4
Filen åpnes for lesing, leses med fscanf
og lukkes etterpå:
>> [fid msg] = fopen('test.dat', 'r'); >> A = fscanf(fid,'%5d',[1 inf]) A = 1 5 3 4 >> status = fclose(fid);
Her brukes [1 inf]
for å angi at dataene skal leses inn som én rad. Matlab gjenbruker formatet'%5d'
for hvert element i A
.
Funksjonen fscanf
kan ikke brukes til innlesing fra tastatur. I motsetning til fprintf(1,...
for å skrive ut på skjermen har ikke fscanf(0,...
noen mening. For innlesing fra tastaturet i måinput
benyttes.