Programmering01 [prrprr01]

Moment05 - Datapersistens

Introduktion

I detta moment skall vi kolla på lite olika tekniker för datapersistens. Datapersistens innebär att vi lagrar data så att det blir beständigt i någon form och är tillgängligt vid ett senare tillfälle. Olika programmeringsspråk har olika tekniker för detta. Vi skall titta på hur vi lagrar data i filer, sessioner, cookies. Vi kommer också gå igenom grunderna i hur vi jobbar med formulär så att det blir möjligt för användaren att mata in data till våra applikationer.

Databaser är också ett vanligt sätt att lagra data på men det kommer vi inte göra inom ramen för denna kursen. Vill du lära dig den tekniken på egen hand så hänvisas du till kursen Webbserverprogrammering01.

Momentets mål

I varje moment så jobbar vi mot ett eller flera mål som skolverket har satt upp i varje kurs.

Centralt innehåll

  • Grundläggande programmering i ett eller flera programspråk varav minst ett av språken är textbaserat.
  • Strukturerat arbetssätt för problemlösning och programmering.
  • Grundläggande kontrollstrukturer, konstruktioner och datatyper.
  • Arbetsmetoder för förebyggande av programmeringsfel, testning, felsökning och rättning av kod.
  • Grundläggande datastrukturer och algoritmer.
  • Gränssnitt för interaktion mellan program och användare.

Filhantering

Filhantering är oftast det enklaste sättet att spara mindre mängder data på. Ibland kan en utvecklare ha behov av att bara dumpa ut felmeddelande eller variablers värde för att skapa en loggfil. Jag går nedan igenom hur du både läser från en fil och skriver till en fil. Eftersom det finns med effektiva tekniker, läs databaser, för att spara större datamängder så väljer jag att inte göra allt för avancerade saker med filerna utan nöjer mig med grundtekniken att läsa och skriva. Vi skall dock se på lite olika sätt som filen kan behandlas.

En fil på en dator är en sammanhängade mängd data som lagras under ett specifikt namn. Vi kan välja att skapa en fil med massa olika ändelser men eftersom vi i början väljer att skapa textfiler så är det lämpligt att ge våra filer ändelsen .txt, vi skall också undvika att i filnamnet använda å, ä, ö eller mellanslag då det ökar risken att vi får problem i kommunikationen mellan php-skriptet och den fysiska filen.

Om det visar sig att å, ä och ö visas konstigt som innehåll i filen eller konstigt i php/html-sidan när vi har läst in data från filen, så beror detta på att vi inte är tillräckligt tydliga med teckenkodningen. Utgå alltid från att php-filen är sparad med teckenkoden UTF-8 (kolla efter encoding i din editor) och att innehållet i HTML också är deklarerat som UTF-8 (<meta charset="UTF-8">).

Läsa från fil

Jag kommer visa på lite olika sätt som vi kan läsa in data från en fil och sedan skriva ut det på en hemsida. Jag kommer visa på fyra olika sätt hur vi kan jobba med samma fil för att få samma eller olika utskrifter. Filen som vi skall läsa från heter test.txt

test.txt

Programmering01 | Vi lär oss grunderna i programmering
Webbutveckling01 | Vi lär oss skapa webbsidor
Webbserverprogrammering01 | Vi lär oss programmera dynamiska webbsidor

På varje rad finns det information om två saker, dels är det namnet på kursen och en extremt kort kursbeskrivning. Däremellan finns det ett tecken som heter pipe och som i macen skrivs genom alt+7. Tecknet pipe är i detta fall en avdelare eller en delimiter.

A delimiter is one or more characters that separates text strings. Common delimiters are commas (,), semicolon (;), quotes ( ", ' ), braces ({}), pipes (|), or slashes ( / \ ).
http://www.computerhope.com/jargon/d/delimite.htm [2016-10-03]

Dumpad utskrift

Det enklaste sättet att skriva ut något från en fil är helt enkelt att läsa in allt som finns i en fil och sedan skriva ut det exakt som det står i filen. Detta kan vara intressant om vi jobbar med en enkel loggfil men det är då viktigt att texten på något sätt formatteras innan den läggs i filen, annars blir det nästan oläsligt när det skall skrivas ut på hemsidan.

<?php
// lagra filnamnet i en variabel
$filename = "test.txt";

// readfile tar som argument filnamnet och skriver ut innehållet
readfile($filename);
?>
Programmering01 | Vi lär oss grunderna i programmering Webbutveckling01 | Vi lär oss skapa webbsidor Webbserverprogrammering01 | Vi lär oss programmera dynamiska webbsidor

Som sagt, det är viktigt att texten är formaterad på något sätt för att detta skall bli bra.

Läs in filen rad för rad

För att göra detta lite bättre så väljer vi att läsa in en rad i taget hela vägen tills filen är slut.

<?php
$filename = "test.txt";
$file = fopen($filename, "r");

// feof -  end of file in a file pointer
while(!feof($file)){
	echo fgets($file)."<br />";
}
?>

Här skapas en variabel $file som är ett filhandtag och håller koll på filen och även en pekare som pekar på var i filen vi är. Filpekaren kan bara stega framåt så vi kan inte hoppa fram och tillbaka i filen.
Filen öppnas med fopen() och argumentet r talar om för funktionen fopen att den skall öppna filen för bara läsning.
Sedan loopas filen igenom rad för rad så länge som funktionen feof() inte returnerar false.
Funktionen fgets() hämtar den raden som filpekaren just nu pekar på.

Utskriften blir då.

Programmering01 | Vi lär oss grunderna i programmering
Webbutveckling01 | Vi lär oss skapa webbsidor
Webbserverprogrammering01 | Vi lär oss programmera dynamiska webbsidor

Det blev bättre men vi kan fortfarande inte göra så mycket med själva innehållet.

Läs in filen och lagra i en array

Vi skall titta på en annan teknik som inte ger oss en bättre utskrift men vi får en större möjlighet att göra något annat av filen i nästa läge.

<?php
// lagra filnamnet i en variabel
$filename = "test.txt";
$file = fopen($filename, "r");

while(!feof($file)){
	$arr[] = fgets($file);
}

// Loopar igenom hela arrayen
foreach ($arr as $line) {
	// Skriv ut varje rad för sig.
	echo "$line<br />";
}
?>

Vi läser in filen på samma sätt men denna gången så väljer vi att lagra varje rad i en komponent i en array. Här skulle vi kunna tänka oss att vi läser in en mängd namn och sorterar dem innan vi senare skriver tillbaka dessa namnen till filen som en sorterad lista.
Här skriver vi dock ut arrayen på ett sätt så att utskriften i detta exempel blir samma som tidigare.

Programmering01 | Vi lär oss grunderna i programmering
Webbutveckling01 | Vi lär oss skapa webbsidor
Webbserverprogrammering01 | Vi lär oss programmera dynamiska webbsidor

Läs in filen och dela upp i två arrayer

Vi drar nytta av våra kunskaper om arrayer för att läsa dela upp varje rad i två olika arrayer, den ena som innehåller kursnamnet och den andra som innehåller kursinformationen. PHP har en mängd av inbyggda funktioner och här använder vi oss av två bra funktioner. Först explode() som delar upp en sträng i flera olika delar utifrån den delimiter som vi vill dela strängen på. Sedan använder jag en funktion till här som heter list() som i detta fallet tar emot de två delsträngar som explode() returnerar och lägger dessa i var sin array.
Återigen, det viktigaste är inte att kunna detta utantill, ju mer du jobbar med PHP desto bättre känsla får du för vilken typ av funktion som kan finnas inbyggd. Då är det viktigare att kunna söka efter och kunna använda dessa funktioner än att kunna skriva koden utantill.

<?php
$filename = "test.txt";
$file = fopen($filename, "r");

// Skapar två arrayer
$course = array(); $desc = array();

while(!feof($file)){
	// Läser nästa rad till $row
	$row = fgets($file);
	// Om $row har innehåll
	if(strlen($row)>0){
		// Använd explode för att dela upp innehåll i olika arrayer
		list($course[], $desc[]) = explode("|", $row);
	}
}

// Skriv ut innehållet från våra två arrayer.
for($i=0; $i<count($course); $i++){
	echo "<p>Kurs: $course[$i]<br>Beskrivning: $desc[$i]</p>";
}
?>

Det mesta är beskrivet i tidigare exempel eller i texten ovan. Det som kan se lite konstigt ut är att vi på rad 8 loopar så länge som vi inte kommit till slutet av filen och sedan på rad 12 kollar om längden på strängen verkligen är mer än 0. Men detta beror på att funktionen fgets() läster nästa rad och då kan den bli tom om filhandtaget på kodrad 8 pekade på den näst sista raden i filen. För ännu större säkerhet skulle vi på rad 12 också kunna kolla om det verkligen finns en pipe i strängen $row, annars har vi ju inget att köra explode() på.

Kurs: Programmering01
Beskrivning: Vi lär oss grunderna i programmering

Kurs: Webbutveckling01
Beskrivning: Vi lär oss skapa webbsidor

Kurs: Webbserverprogrammering01
Beskrivning: Vi lär oss programmera dynamiska webbsidor

Att tänka på vid inläsning av tal

När vi använder fgets() som funktion för inläsning från fil så kommer raden som vi hämtar att returneras som en sträng. Detta kan ge oss problem när vi skall läsa in tal som vi sedan vill räkna med. Glöm inte bort att i PHP så anger vi inte vilken datatyp en viss variabel skall ha utan detta sköter PHP om automatiskt. Problem kan uppstå när strängen 12 eller heltalet 12 skall hanteras. För att tvinga en sträng att bli ett heltal använder vi funktionen intval(). På samma sätt kan vi använda funktionerna floatval(), för decimaltal, boolval(), för boolean, strval(), om vi vill göra det till en sträng. Vill vi kolla vilken typ en variabel har så talar funktionen gettype() om det.

En lista på alla funktioner som styr variabler hittar du här.

Exempel

<?php
// skapa variabeln $var
$var = "12";
// Skriver ut datatypen på variabeln $var
echo "$var är en ".gettype($var);

// ändrar $var till en int
$var = intval($var);
// Skriver ut datatypen på variabeln $var
echo "<br />Nu är $var en ".gettype($var);

// ändrar $var till en float (decimaltal)
$var = floatval($var);
// Skriver ut datatypen på variabeln $var
echo "<br />Nu är $var en ".gettype($var);

// Vi gör om $var till en sträng
$var = strval($var);
echo "<hr>Nu är $var en ".gettype($var);

// Vilken datatyp har M_PI?
echo "<br>M_PI är en ".gettype(M_PI);

// Om vi multiplicerar en double med en string vad händer då?
$svar = M_PI * $var;
echo "<br />$svar är en ".gettype($var);
?>
12 är en string
Nu är 12 en integer
Nu är 12 en double
Nu är 12 en string
M_PI är en double
37.6991118431 är en string

Som du ser så är det lite lurigt att PHP själv får bestämma vilken datatyp en viss varibel har, det kan vara värt att tänka på om den applikation inte beter sig som du förväntar dig när den skall räkna. Funktionen gettype() är nyttig i dessa fall.

Detta problem är lite specifikt för PHP, de flesta andra språk tvingar dig att tala om redan när du skapar en variabel vilken typ den skall ha, det är lite mer kod vid skapandet men sedan så håller programmet ordning på detta åt dig.

Felhantering om filen inte går att öppna

Om vi provar att öppna en fil som inte automatiskt skapas (mer info om det senare) om den inte finns så kommer vi få ett fel, därför kan det vara bra att kolla att filen finns innan vi försöker öppna den.

<?php
$filename = "test.txt";

// Kolla om filen finns
if(!file_exists($filename)) {
	// Om filen ej hittas avbryts körningen
  exit("Filen kunde inte hittas");
} else {
	// skriv ut filens innehåll
	readfile($filename);
}
?>

Här används bara fgets() och readfile() för att läsa från filen och fopen() för att öppna filen, det går naturligtvis att göra mycket mer med filer. Alla tillgängliga funktioner för filhantering hittar du här.

Uppgift

  1. Nu är det dags för den första uppgiften. Använd en lämplig teknik från genomgången ovan för att läsa in ett antal resultat från en provskrivning, resultaten skall lagras som en fil, ett resultat på varje rad.
    Skriv sedan en kod för att lösa så många av uppgifterna nedan som möjligt. Skriv sedan ut svaren på lämpligt sätt.
    Utgå gärna från att någon som inte läst uppgiften skall förstå svaren.

    1. Skriv ut alla resultaten i en lista.
    2. Skriv ut antalet provresultat.
    3. Skriv ut medelvärdet på provresultaten.
    4. Skriv ut högsta och lägsta resultatet.

    På uppgift c och d så är det inte kodningen som blir svårast utan det är ditt sätt att tänka. Vi kommer i ett senare moment jobba med pseudokod och aktivitetsdiagram som hjälpmedel för att lösa problemet. Denna gången har du inte dessa verktyg men försök ändå att fundera hur du skall tänka rätt lösning innan du styr datorn att räkna ut det åt dig.

Tips till min och max

Hjälp att hamna rätt med uträkningen av min och max. Det finns två sätt att tänka på.

  • Sätt min och maxvärdet till samma värde som den första komponenten har. Sedan jämför du alla andra värden med detta.
  • Det andra alternativet är att sätta variabeln $min till ett så högt värde som det bara är möjligt, vilket innebär att alla tal i arrayen är lägre. Det högsta tal som finns i PHP är en konstant som skrivs PHP_INT_MAX. På samma sätt finns PHP_INT_MIN som då är det lägsta värde som finns.
<?php
echo 'Det högsta värdet för en int i PHP är: '. PHP_INT_MAX;
echo '<br />Det lägsta värdet för en int i PHP är: '. PHP_INT_MIN;
?>
Det högsta värdet för en int i PHP är: 9223372036854775807
Det lägsta värdet för en int i PHP är: -9223372036854775808

provresultat.txt

15
4
14
17
0
18

Denna fil slumpas varje gång du laddar om sidan. 5-20 st resultat mellan 0 och 25 poäng.

Svårare uppgift

Här kommer en lite svårare uppgift om du vill utmana dig själv.

  1. Jobba vidare på uppgiften ovan fast nu skall du lägga till ett namn på varje rad ihop med provresultatet. Bestäm själv hur du vill att raderna skall se ut och vilken delimiter du vill använda.
    Svara sedan på samma frågor som ovan och där det är möjligt så svarar du också vilken person som har det högsta och/eller det lägsta provresultatet.
    Fundera på vad du behöver lagra för information för att lösa denna uppgiften? Vilka variabler behöver du ta hjälp av?

Attribut för öppning av fil

När vi använder funktioner som öppnar filer som tex fopen() så anger vi med ett attribut hur vi vill att den filen skall öppnas. Här kommer en lista på vad de olika attributen betyder.

Attribut Öppnar för... Filpekarens placering Hantering av filen
r läsning
först i filen
r+ läsning och skrivning först i filen
w skrivning först i filen Om filen inte finns skapas den och om den finns så blir den överskriven.
w+ läsning och skrivning först i filen Om filen inte finns skapas den och om den finns så blir den överskriven.
a skrivning slutet av filen Skapar filen om den inte finns.
a+ läsning och skrivning slutet av filen Skapar filen om den inte finns.
x skrivning början av filen Om filen redan existerar returnerar funktionen FALSE och ett varnings meddelande kan komma att skrivas ut.
x+ läsning och skrivning början av filen Om filen redan existerar returnerar funktionen FALSE och ett varnings meddelande kan komma att skrivas ut.
c skrivning början av filen Om filen inte finns så skapas den, om den finns så öppnas den men skrivs inte över.
c+ skrivning och läsning början av filen Om filen inte finns så skapas den, om den finns så öppnas den men skrivs inte över.

Tänk på att den snabbaste regeln är alltid exakt den som du behöver, öppna inte för läsning, eller skrivning, om du inte behöver det.

Skriv till fil

Nu kan vi läsa från fil så då är det dags att skriva till fil. Det finns några saker som du behöver fundera på, hur skall filen se ut, hur skall du skriva till den och skall den skrivas över varje gång eller skall en ny text skrivas efter den redan skrivna texten?
Beroende på svaren ovan så vet du vilket attribut, se avsnittet attribut för öppning av fil, du skall öppna filen med och du vet också vilken delimiter du skall använda.

A delimiter is one or more characters that separates text strings. Common delimiters are commas (,), semicolon (;), quotes ( ", ' ), braces ({}), pipes (|), or slashes ( / \ ).
http://www.computerhope.com/jargon/d/delimite.htm [2016-10-03]

<?php
// lagra filnamnet i en variabel
$filename = "test.txt";

// Vi väljer att öppna filen med attributet a som skapar filen om den
// inte finns och sätter filpekaren sist i en befintlig fil
$file = fopen($filename, "a");

// fwrite används för att skriva en rad till filen
// Notera \n för att skapa en radbrytning
fwrite($file, "Webbutveckling02 | Vi lär oss mer om webbutveckling\n");

// Vi stänger filen med fclose, det måste inte göras men frigör resurser
fclose($file);
?>

Funktionen fwrite() kan ge ett felmeddande om det av någon anledning inte går att skriva till filen. I vårt exempel ovanför så utgår vi från att allt har gått bra. Det kan dock vara bra att kolla så att vi inte får något felmedelande.

<?php
// lagra filnamnet i en variabel
$filename = "test.txt";

// Här skapar vi texten som en variabel
$text = "Programmering02 | Vi börjar programmera spel";

// Vi väljer att öppna filen med attributet a som skapar filen om den
// inte finns och sätter filpekaren sist i en befintlig fil
$file = fopen($filename, "a");

// om fwrite inte lyckas så returneras värdet FALSE
if(!fwrite($file, $text)){											// Om "FALSE" körs if-satsen 
	echo "Det gick inte att skriva till filen";		// Skriver ut ett felmeddande
	exit;																					// Avbryter körningen
}

// Vi stänger filen med fclose
fclose($file);
?>

Uppgift

Exempelkod på hur du slumpar ett tal.

//Slumpa ett tal mellan 0 och 25 som skrivs ut
echo rand(0,25);
  1. Du skall nu skriva till den filen som du jobbade med i uppgift 1. Varje gång du laddar om sidan skall ett slumpat tal mellan 0 och 25 läggas sist i filen. Du kan enklast testa detta genom att kopiera alla kod från förra uppgiften och lägga den efter att ett nytt tal har skrivits till filen.
    OBS: Tänk på att du behöver läsa in filen en gång till för att vara säker på att det nya talet är inskrivet i filen och hämtas på rätt sätt.

Svårare uppgift

Här kommer en lite svårare uppgift om du vill utmana dig själv.

  1. Jobba vidare från uppgift 2 och lägg nu till både ett slumpat resultat och ett namn samt dela av strängen med den delimiter som du har valt.
    TIPS: Skapa en array med namn och slumpa sedan fram ett index och skriv sedan detta namnet till filen.

Session & cookies

Om vi kikar på kunskapskraven för kursen Programmering01 så ingår inte sessions och cookies. För de uppgifter som finns i denna kurser är det heller inget krav att kunna dessa teknikerna för att lösa uppgifterna.

Men....

Flera lite större uppgifter löses enklare med, framförallt, sessioner men också med cookies. Extremt kortfattat så fungerar sessioner som en variabel som lever över flera sidor. Cookies är istället en liten fil som är lättare att skriva till och läsa från än en vanlig fil.

Om du tror att detta kan vara användbart så länkar jag här till sessions & cookies som presenteras i kursen Webbserverprogrammering01.

Kodexempel

Tillsammans med en elevgrupp så byggde vi gemensamt en enklare todo-applikation där en användare kan skapa punkter som sedan bockas av allt eftersom de är färdiga. Här jobbade vi med formulär och filer för att lösa uppgiften. Arbetet gjordes under två lektioner och sedan finns det ytterligare inlägg med tips på alternativ kod.

Länk till alla inläggen.

Redovisning

  1. Lägg upp dina övningar på din hemsida. Lägg dem på en sida med tydliga förklaringar eller skapa flera sidor i en mapp med olika uppgifter. Viktigt att jag, och andra, kan gå mellan sidorna så vi inte missar något.
  2. Länka sidan/sidorna från din samlingssida.
  3. Länka också till CSource och se till att jag den vägen kan se all din kod.
  4. I Wordpress så reflekterar du över ditt arbete, berätta vilka uppgifter du gjort och varför.
    Frågestöd
    • Hur har momentet gått? Skriv lite om hur du tycker att det fungerar att bygga lite störe program som består av olika delar av det som du har lärt dig ifrån tidigare moment.
    • Vilken del av detta moment var särskilt svårt? Hur gjorde du för att komma över de svårigheterna?
    • Vilka resurser har du använt dig av för att lära dig PHP? Någon av de som jag har länkat eller någon annan bra resurs? Har du tips på någon bra resurs så länka gärna denna.