FileIO

18 April 2020

FileIO

Introduction

Stream

flow between program and IO device/files
input stream, output stream表示flow的方向

file Text file Binary file
encode ascii-encode,通常給人看(可用text editor編輯) 0,1 組成 給電腦看
OS-dependent 沒有OS問題,沒台電腦看到的檔案內容都依樣 OS-dependent不同電腦可能就不能執行binary file
Efficiency 較差 較好(電腦處理快)

Java binary file/byte code : OS-independent的binary file

Text file write

所有File IO的class都在java.io內,所以一開始要import java.io

file open要用try catch比較好,其中outputstream要declare在try block外,不然try外面就不能access這個outputstream variable(變成try block內的local variable)

close

close一定要記得寫,雖然程式正常結束時也會自動幫忙清除開啟的file但是

  1. 程式不一定正常結束,abort時可能不會清除file,或者是buffered資料沒寫入file裡
  2. 如果同個程式內對一個檔案同時讀寫,一定要在寫完後關掉才能讀(read,write lock)

open

// 相當於open file
import java.io.PrintWriter;
import java.io.FileOutputStream;
PrintWriter outputstream = null;
try{
    outputstream = new PrintWriter(new FileOutputStream("a.txt"));
}
catch(FileNotFoundException e){}
outputstream.print/ outputstream.println / outputstream.printfs
outputstream.close();  // garbage collection, free resource
  1. printwriter是用來作 text file的write的class,所以output會跑到後面specify的file
  2. printwriter沒有以檔名當作參數的constructor,所以要用FileOutputStream
  3. 如果a.txt存在,裡面內容全部被刷掉,如果a.txt不存在,程式自動幫忙創一個
  4. 因此開啟時要小心不要把原本檔案內容清掉,可用File class來檢查檔案是否已存在

如果要open file且append(不刪除舊有內容)

new PrintWriter(new FileOutputStream(File_name,true))

println(arg)的arg可以是各種datatype(只要有toString method即可),不同datatype用「+」連接起來

Buffer

IO花時間,所以寧可多搜集一點再一次寫也不要每次來就每次寫,因此會buffer在local的RAM flush()這個function可以強制把data寫進File裡

Read file

Scanner

很好用,只在意input stream,不在意是從哪裡來的file、keyboard之類的
scanner的constructor和Printwriter依樣沒辦法只拿file name當作參數,要透過FileInputStream來呼叫constructor
Scanner依樣有nextInt, nextLine的函式可用
Scanner在java.util package NoSuchElementException, InputMismatchException, IllegalStateException都是unchecked exception,不用特別declare怎麼catch或throw

Scanner inputStream = null;
try{
    inputStream = new Scanner(new FileInputStream("a.txt"));
}
catch(FileNotFoundException){...}
try{
    int a = inputStream.nextInt();
    String line = inputStream.nextLine();
}
catch(NoSuchElementException){...}    //說明沒有nextInt或nextLine
// 或者可以用
while(inputStream.hasNextLine()){inputStream.nextLine();}
inputstream.close();

BufferReader

在java.io內,只有readline跟read兩個選項
readline : 一次讀一行,EOF或沒讀到return null
read一次讀一個char而且會轉成int形式,所以要cast回char。EOF或沒讀到return -1

char next = (char)(inputStream.read());

BufferReader inputStream = new BufferReader(new FileReader(File object/File name))
IOException : IO exception,包括非預期的到達EOF
經過測試: EOF不會丟IOException !!!! 所以還是要用while的寫法

import java.io.BufferReader;
import java.io.FileReader;
try{
    BufferReader inputStream = new BufferReader(new FileReader("a.txt"));
    line = inputStream.readline();
}
catch(FileNotFoundException e){...}
catch(IOException e){...}    //像是EOF,檔案讀到底了

可用StringTokenizer先把字串切割,接著用parseInt之類方法做casting

path

windows的 C:\jimmy\ 在code裡要改寫成 “C:\jimmy\” java能接手either unix或windows系統,所以也可以把windows的path用unix來表現 “C:/jimmy/…”

System

System.in System.out System.err 三種不同輸入輸出,好處是可以有不同的導向,舉例而言,System.out導向file,System.err導向screen

errStream是某個outputStream,像file, screen之類的,以下設定System.err導到outputStream

System.setErr(errStream) 設定System.err的導向

以下為導向的function

$ System.setIn(InputStream s)
$ System.setOut(PrintStream s)    //輸出所以是printStream
$ System.setErr(Printstream s)    //輸出所以是printStream

File class

像是File names的wrapper class,輸入path當constructor參數,可以用

  • exists() 來確保檔案是否存在
  • isdirectory() 來檢查是否為資料夾
File fileobject = new File("a.txt")
while(fileobject.exists()){...}

API reference

Text book p.648

Binary file

用ObjectOutputStream/ObjectInputStream 可以一次讀寫一個byte,也可以把目前的變數轉成byte寫進file裡

ObjectOutputStream d = new ObjectOutputStream(new FileoutputStream("a.txt"));
d.writeInt(4)    // 4轉成byte寫進file裡
d.writeshort,writeFloat,sriteLong
d.writeUTF(String s) //用utf encoding寫string
d.writeObject //把object serialized寫進去file
d.flush() // buffer問題
d.close();

open file

ObjectInputStream 跟PrintWriter依樣,如果檔案不存在create一個,存在的話清光舊檔案的內容 一樣要用try catch(IOException)方式開檔案
ObjectOutputStream的函式writeInt之類的會把資料轉成byte寫進file裡,而且writeInt就像write依樣,不會幫忙換行
ObjectOutputStream對每個primitive type的datatype都有特定的寫出方法

write的時候每個datatype就寫該datatype的byte數進檔案裡,但String沒有固定長度,不知道要寫多長,讀的時候會出事,所以會在String的開頭append一些資訊(像是Strlen)來告訴程式要讀、寫多長的binary

在write方面另一點複雜點事,utf-encoding對於每個字元encode的byte數不依樣,所以這個也要記錄,但ascii都只用一個byte沒問題

read file: ObjectInputStream, 跟ObjectOutputStream有對應的method,有writeInt就有readInt,要小心不要用writeInt結果用readLong讀 ObjectInputStream, ObjectOutputStream的constructor參數都是Stream而不是FileObject或Filename

EOFException

在binary file EOFException就會在程式嘗試讀取EOF之後的地方throw exception,但讀取方式要用ObjectInputStream,EOFException屬於IOException。

但是在text file讀到EOF不會throw EOFException

try{
    while(true){
        number = inputStream.readInt();
    }
}

Read WriteObject

要先把Class serializable才能read write object to binary file readObject時要做casting,cast成classtype class要serializable就是內部每個元素都要serializable

  • 有時候為了security reason不會把某些class設為serializable(避免被寫入或從storage內被讀出,因為有serial number)
public class Someclass implements Serializable{...}

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("a.bin"));
out.writeObject(...)

ObjectInputStream Input = new ObjectInputStream(new FileInputStream("test.bin"));

(Someclass)Input.readObject()

Serializable

Serial number

java會給每個寫入stream(使用ObjectOutStream)的object一個serial number,如果object被這個stream讀寫超過一次,java會只寫serial number給stream,stream應該會有serial number對應的object,就可以減少FileIO傳輸的overhead,read的時候也是,會直接給serial number指向object(reference)

Array

Array也是Object,也可以被serialize

RandomAccessFile

要讀取檔案的某個部份而不是要從頭開始讀,沒有write,read object,可以直接吃filename當constructor參數 第二個參數rw,r代表讀寫狀態,open時如果檔案已經存在,不會把舊有內容刪除

RandomAccessFile hi = new RandomAccessFile("test","rw")

API

p.669,670 差不多read write primitive type getFilepointer, setLength, seek