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:

PrintWriter output = new PrintWriter("resultat.txt");

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.
import java.io.*; public class WriteAFile { public static void main(String[] args) throws IOException { PrintWriter output = new PrintWriter("utdata.txt"); output.println("Sjung o gudinna om vreden som brann hos Peliden Achilles,"); output.println("olycksdiger, till tusende kval för achaiernas söner"); output.close(); } }

Några punkter att notera:

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:

String filename = "utdata.txt"; File f = new File(filename); if (f.exists()) { System.out.println("The file '" + filename + "' exists!"); return; } System.out.println("Creating '" + filename + "'"); PrintWriter output = new PrintWriter(f);

(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:

PrintWriter output = new PrintWriter(new FileOutputStream("utdata.txt", true));

eller, om man vill använda ett File-objekt:

String filename = "utdata.txt"; File f = new File(filename); PrintWrtite output = new PrintWriter(new FileOutputStream(f, true));

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:

String filename = "indata.txt"; // or whatever the filename is FileReader file = new FileReader(filename); Scanner fscan = new Scanner(file);

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:

fscan.close(); file.close();

Ö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:

PrintWriter printWriter = new PrintWriter("myOutputFile", "Windows-1252");

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.

Valid CSS!