Filhantering

I experimentella vetenskaper, som t.ex. vissa delar av fysik, är det mycket vanligt att man sparar experimentdata på filer, ofta hundratals eller till och med tusentals.
Detta är en anledning att Matlab har så många funktioner för in- och utmatning (hantering av filer). Ge kommandot help iofun i Matlab så får du en lång lista med funktioner. I denna laboration kommer du att läsa en speciell sorts filer, nämligen bildfiler, och sedan analysera bilderna. Innan du kan göra det måste du förstå lite om uppbyggnaden av filsystemet under unix, så här kommer några (men långt ifrån alla) detaljer.

Filsystemet i unix (liksom Windows) har en trädstruktur, det är hierarktiskt uppbyggt. I datalogiska sammanhang brukar man rita roten överst och roten markeras med ett snedstreck (eng. slash) / . Direkt under roten finns diverse systemkataloger, t.ex. finns katalogen bin (för binaries) som innehåller exekverbara filer (oftast i binärformat). ls-kommandot, som kan lista filnamn bland annat, ligger här och vi säger att den absoluta vägen (eng. absolute path) till detta kommando är /bin/ls . Om du vill se namnen på de kataloger som ligger under roten, kan du skriva ls / i ett terminalfönster. Ditt shell (antagligen bash) som körs i terminalfönstret kommer då, via en komplicerad process, att starta exekveringen av ls-kommandot. Skriver du ls /bin får de se namnen på alla 146 filerna i bin-katalogen. En annan katalog som ligger under roten är media. Här monteras usb-minnen och CD-skivor. Din hemkatalog finns under katalogen /chalmers/users . Säg att du loggar in med användarnamnet erixon, då är vägen till din hemkatalog /chalmers/users/erixon och det är i den katalogen du hamnar när du loggar in. ls tar en massa flaggor, parametrar. T.ex. ger ls -l en utförlig listning som innehåller rättighets- och datuminformation t.ex. Vi går inte in på detta. Om du vill se alla detaljer läs manualbladet genom att skriva man ls i ett terminalfönster.

Man kan flytta sig i filsystemet med cd-kommandot (cd = change directory). Man kan skriva cd absolut_väg, t.ex. cd /chalmers/users/erixon/Matlab_kurs som byter katalog till /chalmers/users/erixon/Matlab_kurs oavsett vilken katalog man råkar stå i. Den katalog där man står kallas aktuell katalog (eng. current directory eller working directory). Kommandot pwd (print working directory) skriver ut namnet på aktuell katalog. Den absoluta vägen innehåller ofta många tecken, så det är i allmänhet bekvämare att ange en relativ väg (eng. relative path). Om man står i /chalmers/users/erixon och skriver cd Matlab_kurs kommer man till Matlab_kurs-katalogen. Säg att det i Matlab_kurs finns en katalog Lab1. Om man står i /chalmers/users/erixon kan man komma direkt till Lab1 genom att skriva cd Matlab_kurs/Lab1 .

cd .. hoppar upp till nivån ovanför och cd ../.. hoppar upp två nivåer osv. cd ../Prog/data hoppar upp en nivå och sedan ner i Prog-katalogen och därefter i data-katalogen. En punkt, ., markerar aktuell katalog, så cd . innebär att man står kvar i samma katalog (så man skriver normalt inte på detta sätt, men när man t.ex. kopierar en fil från ett ställe till aktuell katalog kan man skriva cp ../Prog/data/data_fil . och använder då punkten). cd utan argument hoppar tillbaka till hemkatalogen (/chalmers/users/erixon i mitt exempel). Tilde, ~, betecknar vägen till ens hemkatalog, så i mitt exempel är ~ ekvivalent med /chalmers/users/erixon . ~användare är vägen till användares hemkatalog (så ~ och ~erixon är ekvivalenta om man är användare erixon).

. och .. är också namnet på två kataloger, som finns i varje katalog. Så när man skriver cd .. hoppar min till katalogen .. som pekar på nivån ovanför. I sin hemkatalog kan man ha många filer vars namn inleds med punkt, punktfiler. Dessa innehåller typiskt inställningar för olika program, det kan också vara kataloger som innehåller många filer och underkataloger, t.ex. .matlab. Ett annat exempel utgörs av katalogen .mozilla som innehåller en underkatalog firefox som i sin tur innehåller en 200 filer (och underkataloger).

Punktfilerna är normalt inte så intressanta att se namnen på och de kan dessutom vara många, jag har över 300 stycken, så ls listar inte dessa filer om man inte använder flaggan a (för all), så ls -a .

ls, cd och pwd finns även som Matlab-kommandon och de fungerar nästan som under unix. I labben behövs också det DOS-kommando, dir, som svarar mot unix-systemets ls. Här följer en kort beskrivning av Matlab-kommandona. För mer hjälp, se Matlabs dokumentation.

ls behöver vi inte i labben, men testa att skriva ls i Matlabs kommandofönster.

cd fungerar som i unix med den skillnaden att ett ensamt cd skriver ut aktuell katalog, man hoppar alltså inte till hemkatalogen. För att göra det skriver man cd ~ . Istället för att skriva cd väg kan man skriva cd(str) där str är en sträng som innehåller vägen. str = cd returnerar aktuell katalog i str.

pwd fungerar som i unix, men man kan också skriva str = pwd och lagra vägen i str.

dir är det mest komplicerade kommandot, men det behövs för labben. Ett ensamt dir fungerar väsentligen som ls. Det vi behöver är dock följande:
d = dir(str), där str är en sträng som innehåller en väg, returnerar en vektor, d, av poster. Varje post har de fem datamedlemmarna (fields):
I labben är vi intresserade av name och isdir.

Följande exempelprogram skriver ut namnen på alla vanliga filer (inte katalogfiler) i aktuell katalog:
% List filenames in current directory.

d = dir;
for k = 1:length(d)
  if ~d(k).isdir
    disp(d(k).name)
  end
end
Ett vanligt problem i programmering är att läsa data från filer och göra någon typ av analys t.ex. av data från fysikaliska experiment. Eftersom det är så vanligt (och dessutom kan komma på tentan) följer här lite detaljer. På labben skall du du dock läsa bildfiler och dessa läser vi på ett annat vis (se Bildbehandlingssidan).

Man skiljer mellan filer som kan läsas direkt av en människa, filer som innehåller vanlig text, textfiler. På engelska kallas sådan filer ofta human-readable eller ascii-files. Den andra varianten kallas machine-redable och kan inte direkt läsas av en människa, filerna måste först konverteras. Man säger, något oegentligt, att filerna har ett binärt format (men det har ju textfiler också). Detta kan tyckas opraktiskt med dessa filer, men fördelen är att de tar mindre plats. Att lagra bitarna i ett tal i dubbel precision kräver ju åtta bytes, men att lagra talet på decimal form, t.ex. 3.141592653589793e+12 kräver 21 bytes i exemplet (en byte för varje tecken). Har man då några miljarder tal så spelar detta stor roll. Dessutom avrundas normalt talet när det konverteras till decimal form. Matlabs eget mat-format är ett binärt format liksom det bitmap-format som används för bilderna i labben. I Matlab kan man skriva och läsa mat-filer med hjälp av save respektive load.

Nu några ord om hur man läser textfiler. Det första man gör att att koppla en filidentifierare (en fid i Matlab-terminologi) till filen. Det gör man med fopen-funktionen. Så här kan det se ut:
fid = fopen('datafile', 'r');  % open datafile for reading
I stället för ange namnet, 'datafile' i exempel, kan man ha en strängvariabel som innehåller en väg till filen. 'r' anger av filen öppnas för läsning (read). För övriga alternativ se hjälpen för fopen. Om filen inte gick att öppna (fel namn, fel rättigheter etc) sätts fid till -1. För att läsa en rad med text från filen skriver vi:
str = fgetl(fid);
Vid filslut, när man läst färdigt, sätts str till talet (inte strängen) -1. Filslut kallas ofta eof, EOF (End Of File) på engelska. Om man vill läsa tal kan man använda fscanf-rutinen. Så här läser man ett tal:
number = fscanf(fid, '%e', 1);
Vid filslut sätts number till [] . '%e' är ett format, vi läser ett flyttal med exponentdel. För fler format se slutet på lab1, den sida som beskriver hur man skriver ut tabeller (rulla till slutet av sidan)

När man är färdig med filen stänger man den med fclose:
fclose(fid)
Här kommer ett litet exempel, en funktion som läser en uppsättning datafiler. Vi tänker oss att datatfilernas namn alla slutar på .dat och att de innehåller ett flyttal. Funktionen bildar summan av talen i alla filerna. Notera att dir-kommandot kan användas för att ta ut en delmängd av filerna i en katalog, i detta fall alla filer vars namn slutar på .dat.
function s = file_sum

d = dir('*.dat');  % Look only at filenames ending in .dat.
if length(d) == 0  % No files?
  warning('*** Did not find any files.')
  s = [];          % We must return something.
  return
end

s = 0;
for k = 1:length(d)
  if ~d(k).isdir  % Just in case a directory ends with .dat.
    file = d(k).name;
    fid = fopen(file, 'r');
    if fid == -1
      error(['*** Could not open file: ', file])  % Fatal error.
    end

    number = fscanf(fid, '%e', 1);     % Read first number.
    while ~isempty(number)
      s = s + number;
      number = fscanf(fid, '%e', 1);   % Read one number.
    end

    fclose(fid);
  end
end
 Back