Läsa och skriva filer
Inledning
En fil är ett dokument som är lagrat på datorns hårddisk vilket innebär att den kan existera mer eller mindre permanent. Filer har (vanligtvis) ett namn och ligger i någon mapp (även benämnt katalog eller directory).
Man brukar skilja mellan textfiler och binära filer. Textfilerna innehåller text med någon teckenkodning (ASCII, Latin-1, UTF-8, ...) och kan läsas och skrivas med program som kallas (text-)editorer. Notepad i Windows och Textredigeraren på Mac är exempel på enkla sådana medan Emacs, Vi och TextMate är exempel på mer avancerade editorer. Även programmeringsmiljöer som DrJava, Eclipse och Netbeans innehåller editorer som producerar text-filer som då innehåller programtexten.
Observera att ordbehandlare som Word normalt inte producerar text-filer.
Binära filer är "alla andra" filer. Den fil som produceras när man kompilerar ett Java-program är exempel på en binär fil liksom filer som man (vanligtvis) skriver med ordbehandlare som Word.
Att från program hantera (läsa och skriva) filer kallas för IO som står för Input och Output. När ett program man läser en fil överförs information från filen till variabler i programmet och när ett program skriver en fil överförs information från programmet till filen.
IO är komplicerat.
Dels finns det ett olika filformat, olika teckenkodningar, olika filnamnsregler,
nationella konventioner för tal osv och dels kan olika fel
uppstå.
Detta medför bland annat att det finns en stor mängd klasser med en stor mängd
metoder för att hantera IO.
Här skall vi bara ta upp några av de enklaste sätten.
Vi kommer använda klasserna File
, PrintWriter
, FileReader
samt omnämna
klasserna FileOutputStream
och FileInputStream
.
Dessutom kommer vi använda den sedan tidigare bekanta klassen Scanner
Skriva en fil
Vi kan skriva filer genom att använda ett objekt av typen
PrintWriter
som finns
i java.io
som alltså bör importeras.
Vi måste koppla PrintWriter
-objektet till den fil som vi vill
ha resultatet på. Det kan man göra i konstruktorn t ex så här:
Filen med namnet utdata.txt skapas då i den aktuella katalogen.
Därefter kan man skriva på filen bl a med metoderna print
och println
på samma sätt som skriver på konsolen (i interaktionsrutan i DrJava).
En sak krävs dock för att detta skall gå att köra: man måste deklarera att
den metod man gör detta i kan förorsaka undantag av typen IOException
.
Detta gör man med konstruktionen throws
.
Exempel
Ett program skriver som skriver två rader på filen utdata.txt.Några punkter att notera:
-
Uttrycket
throws IOException
som kommer efter parameterdeklarationerna till metoden. Det anger att metoden (main
i detta fall) eller metoder som metoden anropar kan förorsaka så kallade undantag av ("fel") av typenIOException
. -
Det avslutande anropet till
close
. Om man utelämnar det är det risk att inte alla data blir överförda till filen. Efter det att filen stängts går det inte att skriva på den. -
Den skapade filen heter
utdata.txt -
output
är namnet på referensen tillPrintWriter
-objektet.
Kopiera programmet och kör det. Titta på filen som skapas.
Ovanstående program har ett problem: Om filen utdata.txt
existerar så förstörs den existerande
version när PrintWriter
-objektet skapas (new PrintWriter("utdata.txt")
).
Ofta vill man först kontrollera om filen finns. Detta kan göras om man först skapar
ett
File
-objekt.
Detta är en slags abstrakt fil som man kan använda bland annat för att fråga om motsvarande "riktiga" fil finns. Exempel:
(Här har vi också valt det bättre sättet att definiera filnamnet på ett ställe som en en sträng)
Observera att vi kan använda File
-objektet i konstruktorn till PrintWriter
!
Exempel på andra användbara metoder i File
är delete
och rename
.
Se
dokumentationen
!
Det är också möjligt att i stället för radera eventuellt gammalt innehåll i filen
lägga till rader i slutet.
Om man i programmet ovan vill att rader skall läggas till på slutet skall man
skapa PrintWriter
-objektet
på följande sätt:
eller, om man vill använda ett File
-objekt:
Läsa en fil
För att läsa en fil skall vi använda ett objekt av typen Scanner
.
Det betyder att vi har tillgång till metoder som hasNext
, nextLine
,
nextInt
etc.
Tidigare har vi skrivit något i stil med
Scanner sc = new Scanner(System.in);
för att läsa från tangentbordet.
För att läsa från en fil måste Scanner
-objektet kopplas till filen i stället för
till strömmen System.in
.
Det kan göras på följande sätt:
Därefter kan man läsa som vanligt med scannern (se minilektionen
om Scanner
-klassen).
När filen lästs färdigt bör både scannern och readern stängas:
Övning:
Skriv ett program som frågar användaren efter två filnamn och som sedan kopierar alla rader från den ena filen till den andra. Programmet skall verifiera att utdatafilen inte finns.Olika teckenkodningar*
Det finns ett antal olika sätt att koda tecken på filer.
Detta påverkar i första hand hur nationella tecken (t ex å, ä och ö)
lagras.
Om man vill läsa en fil som har annan kodning än systemets defaultkodning
måste man ange det.
Det kan man göra t ex när man skapar Scanner
-objektet
men då måste man använda klassen FileInputStream
i stället för FileReader
:
FileInputStream file = FileInputStream(filename); | Först och sedan |
Scanner fscan = new Scanner(file, "Windows-1252"); | eller |
Scanner fscan = new Scanner(file, "MacRoman"); | eller |
Scanner fscan = new Scanner(file, "ISO-8859-1"); | eller |
Scanner fscan = new Scanner(file, "UTF-8"); |
Filer som finns på kurshemsidorna är vanligen kodade med
UTF-8
men även den äldre standarden
ISO-8859-1
(ofta kallad Latin 1
) kan förekomma.
Vill man skriva en fil med annan teckenkodning än systemets defaultkodning
kan man ange det när man skapar PrintWriter
-objektet.
Exempel:
Råd: Använd UTF-8 om du själv får välja! Det är den moderna internationella standarden som klarar alla kända teckensystem!
Övning
Skriv ett program som konverterar en fil med en teckenkodning till
en fil med en annan teckenkodning.
Filnamn och teckenkodningar ges läses in eller,
snyggare, ges som parametrar till main
.