Kapitel 7. 7.1 Datatypen char



Relevanta dokument
Programmering Grundkurs (HI1900) Teoridel

Enkla datatyper minne

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Lösningar till uppgifterna sätts ut på kurssidan på nätet i dag kl Tentamen i Programmering C, Fri, Kväll,

Planering Programmering grundkurs HI1024 HT data

Programmeringsteknik med C och Matlab

Struktur: Elektroteknik A. Digitalteknik 3p, vt 01. F1: Introduktion. Motivation och målsättning för kurserna i digital elektronik

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Loopar och datatyper. Föreläsning 3

Johan Karlsson Datavetenskap för teknisk kemi, 10p, moment 1 Datavetenskap Umeå Universitet. Tentamen

Uppgifter till praktiska tentan, del A. (7 / 27)

C++ Lektion Tecken och teckenfält

STRÄNGAR DATATYPEN. Om du vill baka in variabler eller escape-tecken måste du använda dubbla citattecken. strängar

Tecken & Strängar. Kapitel 7

Loopar och datatyper. Föreläsning 3

Agenda. Arrayer deklaration, åtkomst Makron Flerdimensionella arrayer Initiering Strängar Funktioner och arrayer. Övningar nu och då

Alla datorprogram har en sak gemensam; alla processerar indata för att producera något slags resultat, utdata.

Referensguide för streckkoder

Användarhandledning Version 1.2

Dagens föreläsning. Repetition. Repetition - Programmering i C. Repetition - Vad C består av. Repetition Ett första C-program

har du råd med höjd bensinskatt? har du råd med höjd bensinskatt?

Vilken är din dröm? Redovisning av fråga 1 per län

Planering Programmering grundkurs HI1024 HT TIDAA

Föreläsning 11. Strängar

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

Chapter 3: Using Classes and Objects

HI1024, Programmering, grundkurs, 8hp KTH STH TENTAMEN. HI1024:TEN2 - Praktisk tentamen Tid: Fredagen den 21 oktober 2011,

KTH STH TENTAMEN. HI1024:TEN2 - Praktisk tentamen Tid: 8-13, den 18 februari 2012

C++ Slumptalsfunktioner + switch-satsen

Övningsuppgifter till föreläsning 2 Variabler och uttryck

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Fråga 11. Vad skrivs ut? Fråga 12. Vad skrivs ut? Fråga 13. Vad skrivs ut? x=x+y; y=x-y; x=x-y;

Fråga 13. Skriv en loop som fyller arrayen int v[100] med talen

Föreläsning 2. Variabler, tilldelning och kodblock{} if-satsen Logiska operatorer Andra operatorer Att programmera

Övning från förra gången: readword

4 Sammansatta datatyper

Lösningar till tentauppgifterna sätts ut på kurssidan på nätet idag kl 19. Omtentamen i Programmering C, 5p, fristående, kväll,

En kort text om programmering i C.

Föreläsning 13. In- och utmatning

3.3 for-satsen. Programmering, grundkurs, 8.0 hp, Elektro, KTH, hösten Föreläsning 3

Programmeringsuppgifter 1

Programmering i C, 7,5 hp

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Tentamen i Datakunskap NT

Dagens föreläsning. Repetition. Repetition - Programmering i C. Repetition - Vad C består av. Repetition Ett första C-program

1 Texthantering. 1.1 Typen char. Exempel, skriv ut alfabetet

*Pekarvärden *Pekarvariabler & *

Uppgift 1 ( Betyg 3 uppgift )

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Felsökning. Datatyper. Referenstyper. Metoder / funktioner

Deklarera en struct som kan användas för att representera en rät linje

TDIU01 - Programmering i C++, grundkurs

Grundläggande programmering med C# 7,5 högskolepoäng

Strängar. Strängar (forts.)

Switch, Array (fält) switch break, continue, goto (scary) Sammansatta tilldelningar Kommentarer Array Sortering

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Datorteknik 2 (AVR 2)

2 Pekare och dynamiska variabler.

Inledande programmering med C# (1DV402) Summera med while"-satsen

Var tredje svensk saknar eget pensionssparande. Undersökning av Länsförsäkringar 2008

Föreläsning 3.1: Datastrukturer, en översikt

Antal anmälda dödsfall i arbetsolyckor efter län, där arbetsstället har sin postadress

Laboration 1 Introduktion till Visual Basic 6.0

HI1024 Programmering, grundkurs TEN

Mathematica. Utdata är Mathematicas svar på dina kommandon. Här ser vi svaret på kommandot från. , x

Lösningar till uppgifterna sätts ut på kurssidan på nätet i dag kl Omtentamen i Programmering C, Fri, Kväll,

Planering Programmering grundkurs HI1024 HT 2014

Operatorer Tilldelning Kodblock { } if satsen Logiska uttryck Att programmera

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Enklast att skriva variabelnamn utan ; innehåll och variabelnamn skrivs ut

Linköpings Tekniska Högskola Instutitionen för Datavetenskap (IDA) Torbjörn Jonsson, Erik Nilsson Lab 2: Underprogram

5 Grundläggande in- och utmatning

TDIU01 - Programmering i C++, grundkurs

Programmeringsteknik med C och Matlab

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

Att använda pekare i. C-kod

Binär kodning. Binära koder. Tal och talsystem positionssystem för basen 10. Begrepp. Begrepp Tal och talsystem Talomvandling ASCII-kod NBCD Gray-kod

Lösningar till uppgifterna sätts ut på kurssidan på nätet idag kl Omtentamen i Programmering C, 5p, A1, D1, E1, Fri, Pr1, Te/Ek1,

Belopp Belopp > procent

Föreläsning 3-4 Innehåll

Statistik över heltal

Introduktion till algoritmer - Lektion 1 Matematikgymnasiet, Läsåret Lektion 1

Följande, ur problemsynpunkt enkla uppgifter, är till för att nöta in dagens teori.

Programmering, grundkurs, 8.0 hp, Elektro, KTH, hösten 2010

1,3,5,7,9,...,99. Skriv ett program som genererar en multiplikationstabell med följande utseende

Frekvenstabell över tärningskast med C#

Kammarkollegiet Bilaga 2 Statens inköpscentral Prislista Personaluthyrning Dnr :010

i LabVIEW. Några programmeringstekniska grundbegrepp

Kvinnors andel av sjukpenningtalet

Föreläsning 5: Introduktion av pekare

Visual Basic, en snabbgenomgång

PROGRAMMERING-JAVA TENTAMINA

Programmering, grundkurs, 8.0 hp HI1024, TEN1. Fredagen den 2 mars 2012

LÖSNINGSFÖRSLAG TILL Tentamen i objektorienterad programmering i C++ I

TENTAMEN I PROGRAMMERING. På tentamen ges graderade betyg:. 3:a 24 poäng, 4:a 36 poäng och 5:a 48 poäng

Föreläsning 10. Pekare (Pointers)

Antal självmord Värmland och Sverige

Antal självmord Värmland och Sverige

Boken?!?! Vad är ett program? Kompilerande-Interpreterande Programmeringsmiljö Hello World! Att programmera och ett enkelt program Variabler printf

Programmering, grundkurs, 8.0 hp HI1024, omtentamen, TEN1. Tisdagen den 7 juni 2011,

Uttryck, satser och strömhantering

Transkript:

Innehåll Förord 1 Kapitel 7 3 7.1 Datatypen char........................ 3 7.2 Strängar............................. 8 7.3 Filhantering........................... 12 7.4 Övningsuppgifter........................ 24 7.5 Lösningsförslag......................... 32 7.6 Eftersnack............................ 51

7.1 Datatypen char Datatypen char används för att lagra tecken. Ett tecken är något av alla de som finns på tangentbordet plus några till alla är helt enkelt inte synliga eller skrivbara. Vilka tecken som används i systemet framgår av teckentabellen. Den vanligaste och den tabell som tillämpas här kallas ASCII. Tidigare innehöll ASCII-tabellen 128 tecken. Bland dessa fanns då inte, bland annat, de svenska tecknen ÅÄÖåäö. Idag används en ASCII-tabell, utökad till 256 tecken, där bland andra de svenska bokstäverna ingår.,----- _ < ),. // : - )= \\ / Alla tecken \ // : --- \-_; -. man kan komma // : -._ \ \ att behöva finns -----._ // ( \ inte på tangent- _/ \_ //)_ // ] \ bordet./ [ ]_[~~-_ (.L_/., [ ] \_,/ / /,,./ \, / / I== \ /_ ----- -/------ -._/ -o--o---o--- ~~~~~ Till detta har under senare år kommit Unicode, som vi inte studerar när-

mare i denna kurs. Unicode Standard ver 4.0 innehåller inte mindre än 96 447 tecken och kommer i framtiden att betydligt minska det problem som olika, för små, teckentabeller idag åstadkommer. Exempel 1. En liten rutin som visar hur man hanterar tecken i C 1 char c1,c2,c3; 2 c1= Q ; 3 c2=c1+32; 4 printf("%c %c\n",c1,c2); 5 c3= 8 ; 6 if (c3>=48 && c3<=57) 7 printf("8 i 48 och 57\n"); 8 if (c3>= 0 && c3<= 9 ) 9 printf("8 i 0 och 9 \n"); 10 if (c3>= \x30 && c3<= \x39 ) 11 printf("8 i \\x30 och \\x39 \n"); 1 Tre tecken, c1, c2 och c3 deklareras. 2 c1 tilldelas värdet Q. För att skilja Q som tecken från en eventuell variabel Q, så omger vi teckenkonstanten med apostrofer. Apostrofen ska skiljas från akut accent (som i é) och grav accent (som i è), som båda finns på tangentbordet. 3 Datatypen char är egentligen av heltalstyp. En liten sådan som kan hålla tal i intervallet [ 128, 127]. Dessutom finns det en datatyp, unsigned char som likväl är en heltalstyp men med värden i intervallet [0, 255]. Eftersom char egentligen är ett heltal så kan man förstås också räkna med c1. Här ökas värdet med 32. 4 När vi skriver ut tecknet som motsvarar värdet i c1 och c2 använder vi oss av omvandlingspecifikationen %c. Ut kommer här Q och q. Det är ingen tillfällighet att när en versal A till Z ökas med 32 så får man motsvarande gemena bokstav. Detta gäller dock inte för Å, Ä och Ö. 6 Med hjälp av en logiskt villkor kan vi ta reda på om ett tecken ligger i ett givet intervall. Det finns tre olika sätt att ange en teckenkonstant: som ett decimalt heltal, ett ganska ovanligt sätt eftersom man sällan känner till detta värden. 8 Själva tecknet omgivet av apostrofer. Oftast det bästa sättet men ibland omöjligt att utnyttja, eftersom alla de 255 tecknen inte är skrivbara. 4

7.1 Datatypen char 10-11 Som en hexadecimal konstant. Bokstaven x anger att det är basen 16 som gäller. Observera i sista printf-satsen en sak, som egentligen inte har med detta att göra: För att skriva ut ett \ (backslash), skriver man två backslash efter varandra. Mer om detta om en stund. Teckentabell För att du ska få en uppfattning om vilka tecken som ingår i teckentabellen, vilka bakomliggande numeriska värden de har och var i tabellen de är placerade kan det vara lämpligt att köra programmet nedan. Exempel 2. Detta program producerar en teckentabell på skärmen 1 #include <stdio.h> 2 3 void main(void){ 4 int k,n=1; 5 for(k=0;k<=255;k++){ 6 if (k!=13 && k!=10){ 7 printf("%3d %c ",k,k); 8 if(n%11==0) printf("\n"); 9 n++; 10 } 11 } 12 } Tabellen skriver ut 254 tecken och motsvarande ASCII-värde på skärmen. Två tecken, newline (10) och carriage return (13), har utelämnats för att de stökar till utskriften. Variabeln n finns med bara för att hålla reda på tabellens radbyten. Så här presenteras officiellt ASCII-tabellens 128 första tecken 5

ASCII, American Standard Code for Information Interchange 0 1 2 3 4 5 6 7 8 9 0 NUL SOH STX ETX EOT ENQ ACK BEL BS HT 1 NL VT FF CR SO SI DLE DC1 DC2 DC3 2 DC4 NAK SYN ETB CAN EM SUB ESC FS GS 3 RS US SP! " # $ % & 4 ( ) * +, -. / 0 1 5 2 3 4 5 6 7 8 9 : ; 6 < = >? @ A B C D E 7 F G H I J K L M N O 8 P Q R S T U V W X Y 9 Z [ \ ] ^ _ a b c 10 d e f g h i j k l m 11 n o p q r s t u v w 12 x y z { } ~ DEL Escape-sekvenser När vi nu diskuterar tecken och teckenkonstanter är det lämpligt att nämna de så kallade escape-sekvenserna. För flera av de icke skrivbara tecknen och för dem som har andra betydelser i C, finns koder enkla att komma ihåg. Vi har redan använt några av dem i printf-satsens styrsträng. Vi väljer att behålla de engelska förklaringarna. Kod Hex Förklaring \a 0x07 BEL Audible bell \b 0x08 BS Backspace \f 0x0C FF FormFeed \n 0x0A LF LineFeed \r 0x0D CR Carriage return \t 0x09 HT Tab (horizontal) \v 0x0B VT Vertical tab \\ 0x5c \ Backslash \ 0x27 Apostrophe \" 0x22 " Double quote \? 0x3F? Question mark 6

7.1 Datatypen char Att läsa ett tecken Det finns tre speciella funktioner, för att läsa ett tecken från tangentbordet. En av dessa har vi använt oss av redan i kursens första exempel. Exempel 3. De tre olika sätten att läsa in ett tecken från tangentbordet 1 #include <stdio.h> 2 #include <conio.h> 3 void main(void) { 4 char a,b,c; 5 printf("ett tecken: "); 6 a=getchar(); 7 printf("ett till : "); 8 b=getch(); 9 printf("ett sista : "); 10 c=getche(); 11 printf("\ntecknen är %c, %c och %c",a,b,c); 12 } 4 De tre variablerna a, b och c kan hålla reda på ett tecken var och deklareras alltså genom datatypen char. En char tar upp en byte i minnet. 7 Funktionen getchar läser ett tecken från tangentbordet och lagrar det här i variabeln a. Inmatningen avslutas som vanligt med ENTER. 9 Funktionen getch läser också in ett tecken från tangentbordet och lagrar det i variabeln b. I samma stund som tangenten tryckts ned fortsätter exekveringen utan att invänta ENTER. Vilket tecken som matades in syns heller inte på skärmen. Man säger att funktionen inte ekar inmatningen till skärmen. I flera system når man getch genom conio.h, men kan ibland nås genom curses.h i andra. Observera att getch inte tillhör standard C. 11 Funktionen getche fungerar som getch förutom att den ekar det tecken man skrivit in till skärmen innan exekveringen går vidare. getche tillhör heller inte standarden och man får vara glad om den finns när man behöver den. 12 Den omvandlingsspecifikation som används för att skriva ut ett tecken är alltså %c 7

7.2 Strängar På samma sätt som vi tidigare lärt oss att hantera en array med heltal, finns det anledning, en kanske ännu större, att placera flera tecken i en följd i minnet till en så kallad sträng. Exempel 4. En sträng deklareras på motsvarande sätt 1 char ord[10],b[2],mening[100]; Varje cell i strängarna rymmer precis ett tecken (en byte). Strängen ord kan alltså ta emot upp till 10 tecken. Men här uppstår ett litet problem! Om vi vill använda ord till att lagra kortare ord än 10 tecken, så har vi svårt att veta var ordet tar slut. Vi ska ju komma ihåg att ord har plats för 10 tecken och att de celler i slutet av arrayen, som inte används för att lagra ett kortare ord, trots allt har ett odefinierat innehåll. HEJd ##U> @ Det är ju inte svårt för oss, att se vilket ordet är, men hur ska datorn kunna avgöra det verkliga ordets längd? För att lösa detta problem, har man kommit överens om att använda tecknet NUL, nolltecknet, tecknet med ASCII-värdet 0 som stopptecken. Tecknet, finns inte på tangentbordet och måste skrivas in i programkoden genom \0 eller enbart genom talet 0. \0 är alltså mycket viktigt, när man vill lagra strängar av varierande längd i en teckenarray (sträng) med bestämd, deklarerad, längd. Att läsa och skriva en sträng Oftare än att läsa enstaka tecken, vill man läsa en följd av tecken, ett ord, ett namn eller en hel mening. 8

7.2 Strängar Exempel 5. Programmet läser och skriver ut en sträng. 1 #include <stdio.h> 2 void main(void) { 3 char a[10]; 4 printf("ett ord: "); 5 gets(a); 6 printf("ordet är %s ",a); 7 } 3 Vi deklarerar a[10], som följaktligen rymmer en sträng bestående av 10 tecken. Vi kan genom skrivsättet a[i] nå vilken som helst av de 10 tecknen. Allt beror på vilket värde i har. 5 Funktionen gets tar emot tecken från tangentbordet och lagrar i a, med början i a[0]. Det är här viktigt att man inte skriver in en sträng som innehåller fler än 9 tecken. Direkt efter strängen läggs nämligen tecknet \0 in, för att datorn ska kunna veta var strängen tar slut. Detta \0 -tecken tar förstås också upp en plats. 6 Omvandlingsspecifikationen för en sträng är %s. Det känns inte speciellt tryggt att använda gets. Som programmerare, kan man inte övervaka operatörens arbete. Skriver denne in en för lång sträng, skulle det ju kunna leda till att programmet kraschar. Funktionen scanf kan också användas för strängar, även den med samma problem som nämndes ovan, plus ytterligare ett. Exempel 6. 1 scanf("%s",a); Satsen kan inte ta emot texten FEL IGEN eftersom inläsningen avbryts vid mellanslag. Resultatet blir alltså att a endast kommer att innehålla ordet FEL. Är man säker på att indata innehåller strängar utan whitespace, det vill säga främst utan mellanslag, radbyte och tab är det dock fritt fram för scanf. Allt detta kommer vi att kunna råda bot på när vi behärskar konsten att skriva egna funktioner! 9

Så var det en sak till om satsen ovan. Som nybörjare har vi äntligen lärt oss komma ihåg att skriva &-tecknet före variabelnamnen i anrop av scanf, utan att egentligen veta varför. Och så dyker det upp en sats där &-tecknet inte finns med! Förklaringen kommer inte nu heller, lär dig istället att före namn på strängar, ska inget &-tecken skrivas i samband med scanf. Att jämföra strängar I standardbiblioteket string, som vi inkluderar genom #include <string.h> finns många funktioner som hanterar strängar. Först nämner vi strcmp, som används för att jämföra två strängar. Exempel 7. Att jämföra två strängar 1 r=strcmp(a,b) kan ge r tre olika typer av heltalsvärden. Om r blir 0 betyder det att a och b är identiska Om r blir < 0 betyder det att strängen a kommer före strängen b i bokstavsordning Om r blir > 0 betyder det förstås att b kommer före a i bokstavsordning Vad betyder då: först i bokstavsordning? Eftersom det är ett numeriskt värde förknippat med varje tecken, kan man enkelt avgöra vilka tecken som är minst kommer först. De första tecknen i de båda strängarna jämförs och om de är lika går jämförelsen vidare med nästa par. När olikhet uppstår avgör ASCII-tabellen vilken sträng som är minst. Observera att minst i den här betydelsen inte har något med strängarnas längder att göra ANNA är större än ANDERS. 10

7.2 Strängar Exempel 8. Ett exempel som visar hur man jämför strängar 1 #include <stdio.h> 2 #include <string.h> 3 void main(void) { 4 char a[10],b[10]; 5 int jfr; 6 printf("ett namn: "); gets(a); 7 printf("ett till: "); gets(b); 8 jfr=strcmp(a,b); 9 if (jfr<0) 10 printf("det första är minst\n"); 11 if (jfr==0) 12 printf("de är lika\n"); 13 if (jfr>0) 14 printf("det andra är minst\n"); 15 } 2 Vi inkluderar string.h för att kunna använda strcmp (string compare). 4 Deklaration av två strängar, a och b som rymmer strängar med upp till 9 tecken plus \0 6-7 Inläsning av två strängar, a och b. 8 Anropet av strcmp ger som resultat ett heltal, som lagras i jfr. 9 Om jfr har fått ett negativt värde betyder det att strängen a kommer före strängen b, i bokstavsordning. 11 Detta är kanske den vanligaste testen. Är de två strängarna lika? 13 Den tredje möjligheten att strängen b kommer före a i bokstavsordning. H Vilket värde får jfr om vi i tur och ordning skriver in Anna och ANNA? Eftersom N kommer före n i ASCII-tabellen kommer jfr att få ett värde > 0. H Normalt lagrar man inte först, resultatet av en jämförelse, i en variabel för senare test, utan skriver troligtvis direkt if (strcmp(a,b)==0)... som är sant om a och b är lika. 11

7.3 Filhantering Hittills har vi, för det mesta, försett våra program med data genom att skriva in värden från tangentbordet och några gånger genom att låta programmet förse sig själv, med hjälp av slumptal. Ett steg närmare verkligheten kommer vi nu genom filhantering att läsa indata från och skriva utdata till filer. Genom att ha data lagrad på filer, ofta vanliga textfiler, skapade med en vanlig texteditor eller genom ett annat program, kan genomsökandet av stora datamängder göras mycket snabbt. Förutom textfiler finns också binärfiler, men de lämnar vi så länge. En textfil kan, som här, innehålla data om ett antal personer ett litet personregister. Vi antar att uppgifterna nedan finns lagrade på en fil med namnet PERSON.DAT. Någon har skrivit in dem med hjälp av en texteditor och lagrat filen på samma sätt som vi normalt lagrar programfiler. Det är förstås mer naturligt att ge dem filtillägget.dat eller.txt. Tillägget.CPP eller.c vore direkt förvirrande. Tittar vi närmare på utdraget ur filen i exemplet nedan förstår vi genast att den andra raden anger förnamn och den tredje efternamn. Men sedan blir det svårare. Vad kan 38 och 16500 betyda? Bara den som skapat datafilen kan ge svar. Vi bestämmer att den tredje raden betyder ålder och den fjärde månadslön. Det är tydligt att för varje person finns fyra uppgifter. En person i detta register utgör en post. I en annan fil kan en post vara ett hus, en trafikolycka eller ett land. För den här typen av filer är det viktigt att varje post innehåller lika många uppgifter. En uppgift i en post kallas fält. I exempelfilen har varje post fyra fält: förnamn, efternamn, ålder och månadslön. Det är dessutom viktigt att fälten ligger i samma ordning inom varje post. 12

7.3 Filhantering Exempel 9. Början på vår fil i exemplet rad för rad 100 Kalle Persson 38 16500 Pelle Olsson 42 18350 Sven Johansson 62 17000 Den första raden anger hur många poster filen innehåller. Filen består alltså av uppgifter om 100 personer, inklusive uppgiften om antal, 401 rader. Det är inte nödvändigt att ha den inledande uppgiften om antalet i början av filen. Det finns andra möjligheter att ta reda på när filen har lästs till slut, som vi återkommer till senare. Vad finns på filens 35:e rad? Eftersom vi vet hur många rader (fält) varje post består av och i vilken ordning fälten räknas upp, kan vi slå fast att på rad 35 finns uppgift om ett efternamn. Vilket det namnet är kan vi dock inte ta reda på utan att läsa filen och samtidigt räkna rader fram till den aktuella. Vilken fält tillhör det 2000:e tecknet i filen? Den frågan kan inte direkt besvaras för denna typ av fil eftersom antalet tecken i samtliga fält kan variera. Om däremot alla personer hade haft ett förnamn på 5 bokstäver och ett efternamn på 8, ett 5-siffrigt belopp för månadslönen och två siffror i åldern hade denna beräkning varit möjlig. Detta hade dessutom betytt att vi kunnat räkna ut var den 53 posten finns i filen och snabbt kunnat förflytta oss dit för att läsa in den. Nu är vi istället tvingade att läsa från filens början, fält för fält. Vi måste alltså bläddra förbi 1+52 4 = 209 rader innan vi når den aktuella informationen och hur många tecken detta motsvarar har vi ingen aning om. 13

Att läsa från en fil Följande måste man känna till för att kunna läsa information från en fil: Hur en fil deklareras Hur en fil öppnas Hur man läser från en fil Hur man stänger filen Hur man tar reda på när filen har lästs till slut Den sista punkten ovan är alltså inte så viktig just nu, om filen inleds med att ange hur många data eller poster den innehåller. Exempel 10. Ett enkelt exempel förklarar: Filen innehåller heltal, som ska summeras. Varje post innehåller alltså bara ett fält, talet självt. Filen inleds med att ange hur många tal det finns i filen. 1 #include <stdio.h> 2 void main(void){ 3 FILE fil; 4 int tal,summa=0,antal,i; 5 fil=fopen("tal.dat","rt"); 6 fscanf(fil,"%d",&antal); 7 for(i=1;i<=antal;i++){ 8 fscanf(fil,"%d",&tal); 9 printf("%4d\n",tal); 10 summa=summa+tal; 11 } 12 printf("summan= %d",summa); 13 fclose(fil); 14 } 3 Datatypen FILE är ny för oss. Det är en datatyp även om den inte finns med i tabellen i kapitel 4. fil är vårt eget namn. Asterisken (*) placeras omedelbart framför namnet. Vad som finns bakom deklarationen och variabeln fil, funderar vi inte vidare kring just nu. 5 I denna sats talar vi om vad filen heter ett vanligt filnamn skrivet med MSDOS-regler vilken typ av fil det är samt hur vi ska använda 14

7.3 Filhantering den. Det sista argumentet, "rt" talar nämligen om för programmet att vi tänker läsa från en textfil, (r som i read och t som i textfil). Det är fullt möjligt att ange en path fram till filen, men kom då ihåg att till exempel C:\PROG\A.DAT skrivs C:\\PROG\\A.DAT. Man använder nämligen dubbla backslash i C för att uttrycka ett backslash. (se tabell 7.1) Funktionen fopen, som tar emot de argument vi diskuterat ovan, öppnar filen och gör den redo för läsning. 6 Först läser vi in det tal från filen, som anger hur många tal den innehåller, och lagrar i antal. fscanf är en ny bekantskap, men ändå en gammal. Det är nämligen bara namnet och första parametern, i vårt exempel fil, som skiljer sig från scanf. 7-11 Nu, när vi vet hur många tal vi ska läsa, är det bara att skapa en for-loop, som för varje varv läser in ett tal från filen till variabeln tal. 8 Den första parametern i fscanf anger varifrån vi ska läsa. I scanf är det alltid underförstått tangentbordet vi läser ifrån. Vi vet att filen innehåller heltal så därför kan vi lugnt skriva "%d". Skulle innehållet vara något annat kommer programmet inte att fungera. 9 Det är enbart av pedagogiska skäl vi skriver ut det just inläsa talet på skärmen. Det är förstås inte nödvändigt för att summera dem. Innehåller filen 1000 tal är det förresten inte längre speciellt pedagogiskt. 13 Ordningsamma som vi är, stänger vi filen med fclose. Den fil som vi öppnade med fopen 15

Mer läsning från fil Exempel 11. Vi tar ett exempel till 1 #include <stdio.h> 2 int main(void) { 3 FILE fil; 4 int alder,antal=0,stlk,i; 5 char namn[10]; 6 fil=fopen("kompis.dat","rt"); 7 fscanf(fil,"%d",&stlk); 8 for(i=1;i<=stlk;i++){ 9 fscanf(fil,"%s",namn); 10 fscanf(fil,"%d",&alder); 11 if (alder>20) { 12 printf("%s\n",namn); 13 antal++; 14 } 15 } 16 printf("totalt %d personer",antal); 17 fclose(fil); 18 } 3 Det interna namnet på den fil vi kommer att använda i programmet kallar vi även denna gång för fil. Den här gången ska vi läsa från en fil som heter kompis.dat. Man måste känna till hur denna fil är uppbyggd för att ha chansen att läsa uppgifter från den. Den innehåller poster med två fält. Första fältet innehåller namn och det andra ålder. Varje data upptar en egen rad. Först i filen finns ett tal som anger antalet poster på filen. 5 Vi deklarerar en sträng med plats för 9 tecken samt \0 -tecknet. Underförstått betyder det att vi vet att inget namn är längre än 9 tecken. Det är generellt ett problem, när man ska avgöra hur många tecken vi ska göra plats för i ett fält. Vet du hur många bokstäver det längsta efternamnet i Sverige innehåller? 6 Filen öppnas på samma sätt som i förra exemplet. Det är bara filens namn som har ändrats. 7 Vi läser in stlk, som håller reda på hur många poster filen innehåller. 16

7.3 Filhantering 8-15 Vi konstruerar sedan en for-sats som snurrar stlk varv. 9-10 För varje post ska vi läsa två uppgifter, som vi gör genom två fscanf-satser. Den första uppgiften är ett namn och vi ska läsa en sträng. Det är därför vi använder %s som omvandlingsspecifikation. Observera dessutom att det inte finns något &-tecken framför namn. Det ska det inte heller göra, då variabeln i scanf-satsen är en array av tecken. Dessutom kan vi konstatera att SVEN TURE inte finns med i registret inga namn som innehåller whitespace kan läsas korrekt. Men är hans namn SVEN-TURE går det bra. 11-14 I vårt program vill vi skriva ut namnen på de personer som är över 20 år och dessutom hålla reda på hur många de är. 16-17 Efter att ha skrivit ut hur många kompisar vi fann, stänger vi så filen. Att skriva till en fil Att få resultatet av en körning på skärmen är i verkligheten sällan tillräckligt. På något sätt vill man dokumentera det hela i en fil, som kanske ska vidare till en ordbehandlare eller direkt skrivas ut på en skrivare. Någon gång kanske man vill skapa en datamängd, för användning i ett annat program. Exempel 12. Vi skriver till en fil 1 #include <stdio.h> 2 void main(void){ 3 int k; 4 FILE ut; 5 ut=fopen("fil1.dat","wt"); 6 for(k=1;k<=1000;k++) 7 fprintf(ut,"%3d",rand()%100); 8 fclose(ut); 9 } 4 Deklarationen går till som på vanligt sätt, här bara med den skillnaden att vi kallar vår fil för ut. 5 Om en fil med samma namn, fil1.dat, som den som öppnas här (skapas här), redan finns i katalogen, kommer den äldre att tas bort och lämna plats för den senare versionen. Bara som en varning. Att 17

vi skriver "wt" betyder förstås att vi tänker skriva på filen, w som i write, och t för att det handlar om en textfil. 6-7 I detta exempel fylls en fil med 1000 slumptal mellan 0 och 99. Inte speciellt överraskande finns det en motsvarighet till printf, fprintf, som vi här använder för att skriva till filen. Precis som hos fscanf är dess första parameter filen till vilken vi ska skriva. 8 Glömmer vi stänga en fil, som vi endast läst från, så betyder det inte så mycket. Däremot är det viktigt att stänga en fil, som vi skrivit till. Utelämnas fclose(ut) kommer oftast delar av filens innehåll att gå förlorat det blir kvar en del data i bufferten. En fil finns som bekant på hårddisken. När vi från programmet skriver till filen, skriver inte datorn ut den till hårddisken tal för tal, utan en mellanlagring sker i en buffert i RAM-minnet. Inte förrän denna buffert blir full, skrivs alla tal som lagrats där, i ett svep, ut till disken. Är bufferten stor, behövs därför inte så många diskaccesser. När filen stängs, vi slutar att använda den, skrivs resterande innehåll i bufferten till hårddisken. De sista talen kommer dock aldrig att skrivas till filen om exekveringen avbryts innan fclose exekverats. Att läsa igen I det här exemplet ska vi läsa från den fil som vi skapade i exemplet ovan. Vi vill ta reda på hur många gånger de olika talen 1 till 99 förekommer. Dessutom vill vi ta reda på de 1000 talens medelvärde. Exempel 13. 1 #include <stdio.h> 2 #include <conio2.h> 3 int main(void) { 4 FILE in; 5 int tal,k,l,plats,ok,summa=0,frekv[100]; 6 for(k=0;k<=99;k++) 7 frekv[k]=0; 8 in=fopen("fil1.dat","rt"); 9 for(k=1;k<=1000;k++) { 10 fscanf(in,"%d",&tal); 11 frekv[tal]++; 12 summa+=tal; 13 } 18

7.3 Filhantering 4 Vi deklarerar bland annat en vektor frekv, med plats för 100 tal 7-8 Vektorn frekv nollställs 10-14 Eftersom vi denna gång vet hur många tal vi ska läsa använder vi förstås en for-loop. 12 Det tal vi läser in bestämmer vilken cell i frekv, som ska ökas med 1. 1 printf("medelvärdet %f\n",summa/1000.0); 2 for(k=0;k<=9;k++){ 3 textcolor(cyan); 4 for(l=0;l<=9;l++){ 5 tal=10 k+l; 6 cprintf("%7d",tal); 7 } 8 printf("\n"); textcolor(white); 9 for(l=0;l<=9;l++) { 10 plats=10 k+l; 11 cprintf("%7d",frekv[plats]); 12 } 13 printf("\n"); 14 } 15 fclose(in); 16 } 2-14 Den största delen av detta program tas upp av utskriften. Vi vill ha en tabell på skärmen med 10 avsnitt. Varje avsnitt består av två rader en med rubrik och en med frekvenser. 3-7 Här skrivs rubrikerna ut. Funktionen textcolor(cyan) ställer om färgen på texten. Denna funktion når man genom conio.h i en del system och ingår inte i ANSI C. För att få resultatet utskrivet i tidigare vald färg används cprintf (c som i color). 8 Ett radbyte mellan rubrik och resultat och vit färg för texten. 9-12 Här skrivs frekvenserna ut. Vilken cell som ska bidra med värdet är ett samspel mellan k och l. H Observera att då vi tillverkade filen fil1.dat skapade vi inga radbyten efter talen, vilket betyder att alla 1000 talen hamnade på samma rad. Det gör nu ingen skillnad eftersom fscanf liksom scanf avbryter inläsandet av ett tal vid ett whitespace. Mellanslag och radbyte hör båda till denna grupp. 19

Kopiering av sträng För att kopiera innehållet i en sträng till en annan används strcpy. Alla tecken inklusive \0 kopieras från den sändande strängen till den mottagande. Det ligger på ditt ansvar, som programmerare, att se till att mottagande strängen är tillräckligt stor. Över till ett meningslöst exempel: Exempel 14. För att tilldela en strängvariabel ett värde använder man en strängfunktion 1 #include <stdio.h> 2 #include <string.h> 3 void main(void) { 4 char a[10],b[10]; 5 int k; 6 strcpy(a,"kalle"); 7 strcpy(b,a); 8 for(k=0;k<=4;k++) { 9 printf("%c%c",a[k],b[4 k]); 10 } 11 } Först tilldelas strängvariabeln a värdet KALLE. Direkt därefter kopieras KALLE till strängvariabeln b. Men vad kommer att skrivas ut? Jo det obegripliga ordet KEALLLLAEK Vi har nu studerat tre funktioner som tar emot strängar gets, scanf och fscanf alla dessa lägger automatiskt till tecknet \0 sist i strängen. De två funktionerna strcmp och strcpy kräver att strängarna terminerats korrekt, det vill säga att \0 finns sist. Längre fram ska vi manipulera strängar, tecken för tecken. Det blir då viktigt att avsluta strängen riktigt om vi senare vill använda någon av dessa funktioner på strängen. Sortera namn Som vi tidigare redan sett, kan man deklarera matriser, där varje cell är ett tecken, som till exempel 20

7.3 Filhantering Exempel 15. 1 char lista[10][80]; Denna variabel rymmer alltså hela 800 tecken. På vanligt sätt kan man sedan placera in tecken i matrisen, till exempel tabell[5][12]= Å ;. Nu är det möjligt och dessutom vanligare, att för matriser av typen char, hantera en hel rad i taget genom att skriva tabell[8]. Vill vi till exempel ha den utskriven skriver vi Exempel 16. 1 printf("%s",tabell[8]) vilket känns bättre än rutinen Exempel 17. 1 for(k=0;tabell[8][k]!= \0 ;k++) 2 printf("%c",tabell[8][k]); Tidigare har vi lärt oss att sortera tal i en vektor. När vi nu ska sortera ett antal förnamn i bokstavsordning, är principen densamma. Vi använder fortfarande enkel sortering, men trots det behöver vi ett nytt verktyg. Namnen vi ska sortera finns ursprungligen på en fil. Filen inleds med ett tal som anger hur många, (< 100) och alla namn är kortare än 20 tecken. Vi måste läsa in och lagra alla namnen i minnet. Dessutom måste vi enkelt kunna byta plats på två namn som inte ligger i bokstavsordning. Med hjälp av deklarationen char namn[100][20] gör vi plats för 100 stycken 19 tecken långa namn. Så här kan man åskådliggöra det hela. Figur 7.1 visar en lite mindre teckentabell som består av 5 rader och 6 kolumner. Varje rad har plats för en sträng innehållande 5 tecken plus ett \0 -tecken, här några förnamn. Vi kommer åt varje tecken i tabellen genom at ge två index. Om tabellen deklarerats char ord[5][6], når vi K i KALLE genom ord[0][0]. 21

Figur 7.1: Raderna är alltså numrerade 0...4 och kolumnerna 0...5. Första index ger numret på raden och andra, numret på kolumnen. Dessutom kan man alltså hantera en hel rad i taget genom att endast ange ett index, radens. Med ord[3] avses hela namnet BO. Här följer nu programmet som: Läser in namn från en fil Sorterar namnen i bokstavsordning Skriver ut de sorterade namnen till en ny fil Exempel 18. Ett program som sorterar namn 1 #include <stdio.h> 2 #include <string.h> 3 void main(void){ 4 FILE fil; 5 char namn[100][20],tmp[20]; 6 int antal,i,j; 7 fil=fopen("namn.dat","rt"); 8 fscanf(fil,"%d",&antal); 9 for(i=0;i<antal;i++) 10 fscanf(fil,"%s",namn[i]); 11 fclose(fil); 5 Här deklareras teckenmatrisen, som har plats för 2000 tecken. tmp 22

7.3 Filhantering är en vanlig sträng som vi behöver under sorteringen. 7-11 Namnen läses in från filen namn.dat 10 Eftersom en rad i tabellen namn, är att likna vid med en sträng, känns det naturligt att ta emot namnet från filen till en rad i tabellen. 1 for(i=0;i<antal 1;i++) 2 for(j=i+1;j<antal;j++) 3 if(strcmp(namn[i],namn[j])>0){ 4 strcpy(tmp,namn[i]); 5 strcpy(namn[i],namn[j]); 6 strcpy(namn[j],tmp); 7 } 8 fil=fopen("namn2.dat","wt"); 9 for(i=0;i<antal;i++) 10 fprintf(fil,"%s\n",namn[i]); 11 fclose(fil); 12 printf("*** KLART ***\n"); 13 } 1-7 Sorteringsrutinen. Vi känner igen de dubbla for-satserna och ifsatsen. Till skillnad från, när vi sorterade tal, måste vi här använda strängfunktionen strcpy för att byta plats på två strängar i tabellen. 8-11 Utskrift av det sorterade resultatet. Vi använder samma filvariabel, som för inläsningen. Detta är möjligt endast om vi stängt filen efter inläsningen. Observera att vi genom "wt" gör det möjligt att skriva på filen namn2.dat. Det hade varit riskfyllt, åtminstone under utvecklingsarbetet, att skriva över filen med de sorterade namnen, genom att använda samma filnamn. Om programmet första gången det kördes, inte fungerat som avsett, så hade indata gott förlorat. 12 Det enda som händer på skärmen är utskrift av denna text. Kör gärna programmet med den givna filen och studera resultatet. Som du märker är ordningen inte korrekt i slutet av listan. Att ordningen blir Ärland, Åsa, Östen beror på de svenska bokstävernas placering i ASCII-tabellen. Detta kan förstås ordnas, men vi lämnar det problemet just nu. 23

När vi betraktar detta program ser vi direkt att det består av tre delar, inläsning av data, sortering och utskrift av data. Vi riktigt längtar efter att kunna dela upp dessa delar i funktioner eller hur? Lugn det kommer! 7.4 Övningsuppgifter Uppgift 7-1. Nästan samma ord Textfilen ORD6.TXT innehåller svenska ord, alla med sex bokstäver. Du ska skriva ett program som tar emot ett ord på sex bokstäver, som nödvändigtvis inte behöver finnas bland orden i filen. Utifrån detta ord ska du finna alla ord i filen, som är sådana att de skiljer sig från det ursprungliga ordet med exakt en bokstav. Filen inleds med ett tal som anger hur många ord det finns i den. I orden ingår endast gemener (små bokstäver) a - ö inklusive w. Programmet ska lista de funna orden tillsammans med en avslutande rad som anger hur många ord som hittades. Vilket ord: behöva bedöva behövd behövs behövt beröva Det finns 5 ord som matchar Uppgift 7-2. Dubbletter På filerna data1.dat och data2.dat, av texttyp, finns heltal, i intervallet [1...1000]. Först i filen anges hur många tal den innehåller. En del av talen i intervallet förekommer inte någon gång i filen, andra förekommer en gång. Endast ett tal förekommer precis två gånger. Skriv ett program som tar reda på vilket detta tal är. 24

7.4 Övningsuppgifter Programmet ska inledningsvis fråga efter filens namn och avslutas med att skriva ut det efterfrågade talet. Filens namn: data1.dat Det eftersökta talet är 382 Ett visst mått av effektivitet måste finnas hos programmet, som till exempel att endast en genomgång av datafilen får göras. Uppgift 7-3. Litet personregister Figur 7.2: Hur många i registret heter Bertil? På filen FOLK.DAT finns uppgifter om ett antal människor. Din uppgift blir nu att svara på några frågor vars svar man kan finna genom att gå igenom registret. Vad innehåller registret? Filen FOLK.DAT är en textfil. Om varje person i registret finns fem uppgifter. Varje uppgift tar upp en rad i filen. Finns det n personer i registret så innehåller alltså filen 5n rader. Uppgifterna är i tur och ordning Förnamn Efternamn Födelsedatum, på formatet ÅÅMMDD Vikt Längd 25

Antalet personer i registret kan avläsas på första raden i filen. Uppgifter. Alla uppgifterna kan lösas under en genomgång av registret. Ett lämpligt arbetssätt kan vara att successivt bygga ut programmet med en uppgift åt gången och testköra däremellan. a Hur många personer finns det i registret? b Vad heter den längsta personen (förnamn och efternamn) i registret och hur lång är han? c Hur många personer i registret heter Bertil? d Vilka människor fyller år idag (för- och efternamn)? Om händelsevis ingen person fyller år idag ändrar du datum till i går eller i morgon. e Ta reda på hur många personer som är överviktiga, enligt regeln: Väger man mer (i kg räknat) än antalet centimeter över 1 meter, så är man för tjock! En person som väger 110 kg och är 196 cm lång är klart överviktig (med 14 kg). f Ta reda på registrets medelålder (med två decimaler). Här räcker det att ta hänsyn till födelseåret. g Skriv ut en tabell med 12 rader (en för varje månad), som talar om hur många i registret som är födda just den månaden. Observera att ingen del av registrets innehåll ska hårdkodas, det vill säga programmet ska fungera lika bra om filen FOLK.DAT byts ut mot ett annat register med samma uppbyggnad. Uppgift 7-4. Enkät Textfilen ENKAT.DAT innehåller resultatet av en enkätundersökning. Varje rad i filen behandlar en intervjuad person. Här följer en nyckel för de olika positionerna på raden Kolumn 1 Kön man=1, kvinna=2 2-3 Ålder två siffror 4-5 Röker antal cigaretter/dag 26

7.4 Övningsuppgifter Då antalet cigaretter/dag understiger 10 finns siffran 0 i kolumn 4. Skriv ett program som besvarar följande frågor. Samtliga procentsatser ges i heltal. Hur många procent är rökare? Hur många procent av kvinnorna är rökare? Hur många cigaretter/dag röker de utfrågade i medeltal? Hur många cigaretter/dag röker de rökande i medeltal? Hur många cigaretter/dag röker de rökande männen i medeltal? Figur 7.3: Uppgiften handlar om Sveriges kommuner Uppgift 7-5. Kommunstatistik På filen KOMMUN.DAT finns lagrat följande uppgifter om Sveriges samtliga kommuner i början av 1980-talet: Kommunens namn (< 30 tecken utan whitespace) Kommunens folkmängd (heltal) Kommunens preliminära kommunala utdebetering (kommunalskatten, ett decimaltal) Kommunens länstillhörighet. (länsbeteckning bestående av en stor bokstav utom i två fall där beteckningarna är BD och AC) Observera att datafilen har blivit ganska ålderstigen sedan registret konstruerades och att den inte längre översensstämmer med den situation som råder idag. Vid denna tid var Sverige indelad i 24 län. 27

1 Bestäm Sveriges folkmängd. Resultatet presenteras på en rad: Sveriges folkmängd är? 2 Bestäm folkmängden i ett givet län. Programmet frågar först efter en länsbeteckning och presenterar sedan resultatet genom:? län har? invånare 3 Sök kommun med högsta kommunalskatt i givet län. Programmet frågar först efter en länsbeteckning och presenterar svaret med en rad liknande: Högsta skatten i? är? kr och finns i?. 4 Sök det län som har största folkmängden. Programmet svarar genom en rad med följande utseende:? län har? invånare. 5 Skriv ut en lista där varje rad innehåller en länsbeteckning och antalet kommuner i motsvarande län. Tabell över svenska län förr och nu Län då Kod då Län nu Kod nu Stockholms A Stockholms AB Uppsala C Uppsala C Södermanlands D Södermanlands D Östergötlands E Östergötlands E Jönköpings F Jönköpings F Kronobergs G Kronobergs G Kalmar H Kalmar H Gotlands I Gotlands I Blekinge K Blekinge K Kristianstads L Skånes LM Malmöhus M Skånes LM Hallands N Hallands N Göteborgs och Bohus O Västra Götalands O Älvsborgs P Västra Götalands O Skaraborgs R Västra Götalands O Värmlands S Värmlands S Örebro T Örebro T Västermanlands U Västermanlands U Kopparbergs W Dalarnas W Gävleborgs X Gävleborgs X Västernorrlands Y Västernorrlands Y Jämtlands Z Jämtlands Z Västerbottens AC Västerbottens AC Norrbottens BD Norrbottens BD 28

7.4 Övningsuppgifter Antalet län i Sverige har under senare år reducerats från 24 till 21. Den länskod, som förr genom bilarnas registreringsskyltar var allmänt kända, finns fortfarande kvar, men med blygsammare användning. Den fil som här används är kodad enligt den gamla uppdelningen. Uppgift 7-6. Kommuner i Sverige då och nu På filen 06TALET.DAT finns namnen på de kommuner som finns i Sverige idag. På filen 80TALET.DAT finns namnen på de kommuner som fanns i Sverige i början av 80-talet. Vi vet inte, men vi tror att en del kommuner har försvunnit sedan dess och att andra har kommit till. Skriv ett program som under rubriken FANNS 1980 MEN INTE 2006 listar de kommuner som inte finns längre och som under rubriken FINNS 2006 MEN INTE 1980 listar de som uppstått sedan 80-talet. Båda filerna inleds med ett tal (< 300) som anger hur många namn, som finns på var och en av filerna, därefter följer ett kommunnamn på varje rad. Inget namn innehåller fler än 20 tecken och heller inget whitespace. Uppgift 7-7. Byråkratsvenska Fras 1 Fras 2 Fras 3 När det gäller konstruktiv information Beträffande konsekvent rationalisering Mot bakgrund av objektiv effekt Rörande seriös samordning I termer av subjektiv instruktion Under påverkan av konventionell ökning Parallellt med dynamisk kravanalys Fras 4 Fras 5 Fras 6 generaliseras principiella rutiner verifieras reella strukturer utvecklas fundamentala tendenser förordas latenta hypoteser komprimeras godtyckliga ideologier modifieras informella konklusioner realiseras ointressanta restriktioner 29

Fras 7 Fras 8 i samband med administration. med hänsyn till näringslivet. inom ramen för myndigheten. med avseende på personalen. i vad avser intressenterna. i fråga om utbudet. direkt riktat mot utfallet. I tabellerna ovan ser du fraser och ord som ofta förekommer i den svenska som används av politiker och byråkrater. Fraserna är ordnade så, att genom i tur och ordning fritt välja ett ord eller en fras ut varje kolumn bildar man en mening. Eftersom man åtta gånger ska välja från sju fraser så kan 7 8 = 5764801 olika meningar bildas! Ungefär hur många böcker blir det? Din uppgift är nu att skriva ett program som skriver ut 200 slumpmässiga meningar. Innehållet i tabellerna finns lagrad på filen byrakrat.dat. Filen innehåller 56 rader och börjar med de 7 fraserna i kolumnen Fras 1 och så vidare, kolumn för kolumn. Utskriften kan med fördel skrivas ut till en textfil, som sedan till exempel kan importeras till Word. Genom att krydda anrättningen med några egna meningar och lämpliga rubriker, får man ganska snabbt ett dokument som kan användas i samhällsdebatten. Uppgift 7-8. Tärningskast Skriv ett program som kastar två tärningar ett givet antal gånger och därefter presenterar ett liggande stapeldiagram över antalet gånger de olika ögonsummorna uppkommit. Ett körningsexempel: Antal kast: 83 2 ** 3 **** 4 ******** 5 ********* 6 ************ 7 *************** 8 *********** 9 *********** 10 ****** 11 **** 12 * 30

7.4 Övningsuppgifter Uppgift 7-9. ISBN Alla böcker har idag ett ISBN-nummer, International Standard Book Number. Detta består av 10 siffror. Landskoden (2 siffror. Sverige har 91) Förlagskod Intern förlagsnumrering Kontrollsiffra (1 tecken) bestående av en siffra eller bokstaven X (står för 10) Skriv nu ett program som tar emot ett ISBN-nummer i form av en sträng och kontrollerar om numret är korrekt enligt följande: Multiplicera siffrorna i numret, från vänster till höger med i tur och ordning talen 10,9,8,7,6,5,4,3,2,1. Addera alla produkterna Om summan är delbar med 11 är (troligtvis) ISBN-numret korrekt. Testkörning: ISBN: 9144061714 Numret är korrekt ISBN: 9144061713 Numret är inte korrekt 31

7.5 Lösningsförslag Uppgift 7-1. Nästan samma ord Diskussion: När aktuellt ord är inmatat öppnar man filen ord6.txt, tar reda på hur många ord den innehåller och läser därefter, i en forloop, ett ord i taget. Gå sedan, för varje ord, igenom de sex bokstäverna och jämför dem i tur och ordning med bokstaven i motsvarande position i det inmatade ordet. Om de är lika i exakt fem av positionerna, har man funnit ett av de ord man söker. 1 #include <stdio.h> 2 int main(void){ 3 char ord[7],sokord[7]; 4 int k,m,n,s=0,p; 5 FILE fil; 6 7 printf("vilket ord: "); 8 scanf("%s",sokord); 9 fil=fopen("ord6.txt","rt"); 10 fscanf(fil,"%d",&n); 11 for(k=1;k<=n;k++){ 12 fscanf(fil,"%s",ord); 13 p=0; 14 for(m=0;m<6;m++) 15 if (sokord[m]==ord[m]) 16 p++; 17 if (p==5){ 18 s++; 19 printf("%s\n",ord); 20 } 21 } 22 printf("det finns %d ord som matchar\n",s); 23 fclose(fil); 24 } 32

7.5 Lösningsförslag Kommentarer: 7-8 Aktuellt ord matas in 9-10 Filen öppnas och första raden, som anger hur många ord det finns på filen läses in till n 11-21 För varje varv i den här loopen läses ett ord in från filen och jämförs med aktuellt ord. 13-16 Position för position jämförs de två strängarna. Om innehållet är lika ökas p. 17-20 Om 5 tecken i de båda ordern är lika har vi funnit ett av de ord vi söker och skriver ut det. s, som håller reda på antalet funna ord, ökas. 22 Vi skriver ut hur många ord vi fann. Uppgift 7-2. Dubbletter Diskussion: Skapa och nollställ en array a och använd den som frekvenstabell. Läs in ett tal t i taget från filen och öka a[t] med 1. När alla talen lästs från filen finns det precis en cell i a med värdet 2. 1 #include <stdio.h> 2 int main(void){ 3 int a[1001]={0},k,tal,n; 4 FILE fil; 5 char filnamn[80]; 6 printf("filens namn: "); 7 scanf("%s",filnamn); 8 fil=fopen(filnamn,"rt"); 9 fscanf(fil,"%d",&n); 10 for(k=1;k<=n;k++){ 11 fscanf(fil,"%d",&tal); 12 a[tal]++; 13 } 14 for(k=1;k<=1000;k++){ 15 if (a[k]==2) 16 printf("det eftersökta talet är %d\n",k); 17 } 18 fclose(fil); 19 } 33

Kommentarer: 3 a är frekvenstabellen, som nollställs direkt vid deklarationen. 5 filnamn, en sträng med plats för filnamn inklusive path. Vi antar ett 79 tecken ska räcka för detta. 8 Filen med det inlästa namnet öppnas. 9 Antal tal på filen läses in. 10-13 Loopen som snurrar n varv, läser in ett tal, tal, och räknar upp motsvarande plats i arrayen med 1. 14-17 En loop som går igenom a och letar efter talet 2, som innebär att talet med detta index förekommer två gånger på filen. Loopen snurrar vidare även efter att vi har funnit svaret, vilket kan tolkas som att vi här istället borde ha haft en while-loop. Uppgift 7-3. Litet personregister Öppna filen Läs in antalet personer För varje person i filen { Läs in FÖRNAMN Läs in EFTERNAMN Läs in FÖDELSEDATUM Läs in VIKT Läs in LÄNGD Om denna person är längst hittills { Ändra maxlängden Lagra detta förnamn Lagra efternamn } Ta reda på födelsemånad Öka antalet personer med denna födelsemånad Om personen är överviktig Öka antalet överviktiga Om personen fyller år idag Skriv ut för och efternamn Räkna ut personens ålder och addera till subtotal Om personen heter BERTIL Öka räknare som håller reda på antalet BERTIL } Skriv ut alla efterlysta resultat 34

7.5 Lösningsförslag Diskussion: Vi har sju uppgifter, som vi kommer att lösa genom en enda genomgång av datafilen. Ovan har vi klätt ett förslag i pseudokod. Vi börjar här närma oss gränsen för den mängd kod vi kan ha i main och därför kommer det att bli befriande att i nästa kapitel ta itu med funktioner. 1 #include <stdio.h> 2 #include <string.h> 3 int main(void) { 4 FILE fil; 5 char fnamn[20],enamn[20],maxfnamn[20]; 6 char maxenamn[20],namn[20]="bertil"; 7 int fodelse,antal=0,manad[13]={0},n,i; 8 int k,langd,vikt,fodmanad,max=0; 9 int sumalder=0,sammanamn=0,tjocka=0,datum=1010; 10 fil=fopen("folk.dat","rt"); 11 fscanf(fil,"%d",&n); Kommentarer: H En mängd variabler behövs. Alla av betydelse är genom sina namn, förhoppningsvis, självdokumenterande och ges i förkommande fall lämpliga startvärden. Variabeln manad, en array med plats för ett heltal för var och en av 12 månader, nollställs genom ={0}. I variabeln datum hårdkodar vi dagens datum, månadsnummer och dag. 10 Den för programmet viktiga filen öppnas för läsning. 11 Vi tar reda på hur många personer, som ingår i registret. 35

1 for(i=1;i<=n;i++) { 2 fscanf(fil,"%s",fnamn); 3 fscanf(fil,"%s",enamn); 4 fscanf(fil,"%d",&fodelse); 5 fscanf(fil,"%d",&vikt); 6 fscanf(fil,"%d",&langd); 7 antal++; 8 if(langd>max) { 9 max=langd; 10 strcpy(maxfnamn,fnamn); 11 strcpy(maxenamn,enamn); 12 } 13 fodmanad=fodelse/100%100; 14 manad[fodmanad]++; 15 if(vikt>=langd 100) 16 tjocka++; 17 if(datum==fodelse%10000) 18 printf("%s %s fyller år idag\n", 19 fnamn,enamn); 20 sumalder+=106 fodelse/10000; 21 if(strcmp(namn,fnamn)==0) 22 sammanamn++; 23 } Kommentarer: H Loopen, som läser in de fem uppgifterna om en person i taget. I loopen passar vi sedan på att samla och summera de data som denna person bidrar med. När vi sedan påbörjar nästa varv är det en annan person som gäller och vi kan inte på något enkelt sätt gå tillbaka till tidigare behandlade personer. 7 Det är förstås onödigt att räkna antalet personer, eller varv i en for-loop. 8-12 Uppdaterar de data vi behöver för att ta reda på vem som är längst och hur lång han är. Observera att vi måste använda strcpy för att kopiera namnen till strängvariablerna. 13 Här får vi användning av modulo-räkning och heltalsdivision för att ta fram födelsemånad. 14 Just den månaden ska nu ökas med 1 15-16 Med en enkel formel tar vi reda på om personen är överviktig. 36

7.5 Lösningsförslag 17-18 Årtalet ska inte vara med när vi tar reda på om personen fyller år idag. Just den här rutinen hade vi kunnat göra elegantare genom att ta reda på dagens datum genom datorns klocka. 20 Vilka villkor ska vi ställa på födelseåret för att denna formel ska ge oss personens ålder i hela år? 21-22 Till sist räknar vi antalet BERTIL 1 printf("i registret finns %d personer\n",antal); 2 for (k=1;k<=12;k++) 3 printf("i månad %2d år %2d födda\n",k,manad[k]); 4 printf("längsta personen %s %s är %d cm\n", 5 maxfnamn,maxenamn,max); 6 printf("%4.1f procent eller %d är överviktiga\n", 7 100.0 tjocka/antal,tjocka); 8 printf("medelåldern är ungefär %5.2f år\n", 9 (float) sumalder/antal); 10 printf("%d personer heter %s", 11 sammanamn,namn); 12 fclose(fil); 13 } Kommentarer: H Programmet avslutas nu med utskrift av alla önskade resultat. Den enda svåra beräkningen som behöver göras är uträknandet av andelen (i procent) överviktiga av hela gruppen. En del av resultaten: HILDUR NILSSON fyller år idag I registret finns 200 personer Längsta personen KARL DUVA är 209 cm 40 procent eller 80 är överviktiga Medelåldern är ungefär 65.53 år 6 personer heter BERTIL 37

Uppgift 7-4. Enkät Diskussion: Efter att ha öppnat filen och läst in första raden, som talar om hur många personer n, som ingår i undersökningen, är det dags att ta del av de n enkätsvaren. Visst handlar det om en for-loop? Eftersom enkätsvaren är heltal mellan 10000 och 29999 kan de läsas in som heltal och därefter styckas upp i sina beståndsdelar. Det är lika självklart att en 0-årig röker 0 cigaretter/dag som att det inte finns någon 99-åring som röker fler än 99 cigaretter/dag. Det finns alltså inga riktiga storrökare, > 99 cigaretter om dagen eller personer som är över 99 år i undersökningen. Ett inläst heltal ska delas upp i tre heltal. Ett för kön, ett för ålder, två siffror, och ett för antal cigaretter, två siffror. Detta ordnar vi med modulo och heltalsdivision. Vilka variabler behöver vi då under beräkningens gång för att kunna svara på de fem frågorna? Det vill säga vilka summor måste vi bilda. Antalet personer i undersökningen känner vi redan till. Men vi behöver veta hur många kvinnor det finns och då kan vi också ta reda på hur många män det finns direkt ur detta. Vi behöver veta hur många rökande kvinnor det finns och hur många rökande män det finns. Dessutom behöver vi veta hur många cigaretter som röks per dag av rökarna och hur många av dessa som röks av män. Hoppas att du kan bena upp detta resonemang till lämpliga variabler! Med de summor programmet åstadkommit i loopen kan man nu bilda de fem resultaten. 38

7.5 Lösningsförslag 1 #include <stdio.h> 2 int main(void){ 3 FILE infil; 4 int kon,antal,tal,i; 5 int kvinnor=0,kvinrok=0,manrok=0,personer=0; 6 int cigaretter=0,mancig=0; 7 infil=fopen("enkat.dat","rt"); 8 fscanf(infil,"%d",&personer); 9 for(i=1;i<=personer;i++){ 10 fscanf(infil,"%d",&tal); 11 kon=tal/10000; 12 antal=tal%100; 13 if (kon==2) 14 kvinnor++; 15 if (antal>0 && kon==2) 16 kvinrok++; 17 if (antal>0 && kon==1){ 18 manrok++; 19 mancig+=antal; 20 } 21 cigaretter+=antal; 22 } 23 fclose(infil); 24 printf("%d tillfrågade\n",personer); 25 printf("%.1f %% av gruppen är rökare\n", 26 100.0 (kvinrok+manrok)/personer); 27 printf("%.1f %% av kvinnorna är rökare\n", 28 100.0 kvinrok/kvinnor); 29 printf("%.1f cig/pers/dag i hela gruppen\n", 30 (float) cigaretter/personer); 31 printf("%.1f cig/pers/dag bland rökarna\n", 32 (float) cigaretter/(kvinrok+manrok)); 33 printf("%.1f cig/pers/dag bland rökande män\n", 34 (float) mancig/manrok); 35 } Kommentarer: 3-6 En mängd variabler deklareras och initieras. 7-8 Filen öppnas och antalet personer, som ingår i undersökningen läses in. 9-22 Ett varv för varje person i undersökningen. 39

10 Först läser vi in aktuella data till heltalet tal. Normalt gör man kanske denna inläsning i form av en sträng, speciellt om antalet tecken är större än vad som ryms i ett heltal, eller om svaren inte är siffror. 11 Vi tar reda på vilket kön den här respondenten tillhör. 12 Hur många cigaretter personen röker/dag beräknas. Observera att åldern inte ingår i någon av våra variabler! 13-14 Om det är frågan om en kvinna, så räknar vi upp antalet kvinnor. 15-16 Om det är frågan om en rökande kvinna, röker minst en cigarett/dag, så ökar vi denna variabel med 1 17-20 Om det är frågan om en rökande man. så räknar vi upp, dels antalet rökande män och hur många cigaretter de röker/dag. 21 Här summerar vi antalet rökta cigaretter per dag oavsett kön. 23 Nu ska vi inte läsa från filen längre och kan därför stänga den. 24-34 Utskrift av resultatet, ibland med smärre beräkningar. Uppgift 7-5. Kommunstatistik Diskussion: Här är det meningen att vi ska känna en stor avsaknad av funktioner, eller möjlighet att dela upp programmet i mindre och mer överskådliga delar. Vi skriver ett program som löser en uppgift per körning. Vill man ta reda på flera uppgifter måste därför programmet köras lika många gånger. Inledningsvis frågar programmet efter numret på uppgiften ett tal mellan 1 och 5. Numret på uppgiften går sedan som en röd tråd genom programmets tre switch-satser, en för inläsning av data, en för beräkning och en för utskrift av resultat. 1 #include <stdio.h> 2 #include <string.h> 3 int main(void) { 4 char namn[30],maxkommun[30],lanen[24][3],lan[3]; 5 char sokt lan[3]; 6 int antal=0,k,val,maxk,i,n,kommunant[24]={0}; 7 int folkm,tot=0,lanfolkm[24]={0},flagga,max=0; 8 float skatt,maxskatt=0.0; 9 FILE infil; 40