在之前的文章我們已經可以對本地對檔案和目錄進行新建和刪除等操作,接下來我們來對檔案內對具體內容進行操作。
如下程式碼,我們實現了一個基本的檔案寫入:
1 /** 2 * java.io.RandomAccessFile 3 * 用來讀寫檔案資料 4 * RAF是基於指標進行讀寫的,即RAF總是在指標指向的位置讀寫位元組, 5 * 並且讀寫後指標會自動向後移動 6 * RAF既可以讀取檔案資料也可以向檔案中寫入資料 7 * 8 * @author wjt 9 */ 10 public class RandomAccessFileDemo1 { 11 public static void main(String[] args) throws IOException { 12 /** 13 * RandomAccessFile(String path, String mode) 14 * RandomAccessFile(File file, String mode) 15 * 第二個引數為模式:常用對有 r:只讀模式 rw:讀寫模式 16 */ 17 RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw"); 18 /** 19 * void write(int d) 20 * 寫出給定的int值對應的2進位制的低八位 21 * 00000000 00000000 00000000 00000001 22 */ 23 raf.write(1); // 00000001 24 System.out.println("寫出完畢!"); 25 raf.close(); 26 27 } 28 }
在上面的程式碼中我們可以看出檔案的讀寫想要到 RandomAccessFile,這裡需要注意的是要丟擲異常,否則編譯器會報錯,同時我們用到了它的一個 write() 方法來進行檔案寫入,接收的是一個2進位制的低八位數值,假如我們寫入 raf.write(97),那麼就會被準化為小寫字母 a。
既然能讀,那就能寫,接下來我們將上面寫入的 1 再讀取出來
1 /** 2 * 讀取檔案資料 3 * 4 * @author wjt 5 */ 6 public class RandomAccessFileDemo2 { 7 public static void main(String[] args) throws IOException { 8 RandomAccessFile raf = new RandomAccessFile("raf.txt", "r"); 9 /** 10 * int read() 11 * 讀取一個位元組,並以10進位制的int型返回 12 * 若返回值為-1,則表示讀取到了檔案的末尾 13 */ 14 int d = raf.read(); 15 System.out.println(d); // 1 16 raf.close(); 17 } 18 }
接下來我們再來看一下如何對檔案進行復制
1 /** 2 * 複製檔案 3 * 4 * @author wjt 5 */ 6 public class RandomAccessFileDemo3 { 7 public static void main(String[] args) throws IOException { 8 /** 9 * 建立一個RAF讀取原檔案, 10 * 再建立一個RAF向目標檔案中寫出, 11 * 順序從原檔案中讀取每一個位元組並 12 * 寫入到目標檔案中即可 13 */ 14 // 原檔案,在原檔案中隨便寫一些內容 15 RandomAccessFile src = new RandomAccessFile("raf.txt", "r"); 16 // 目標檔案 17 RandomAccessFile desc = new RandomAccessFile("raf1.txt", "rw"); 18 // 用來儲存讀取到每個位元組 19 int d = -1; 20 long start = System.currentTimeMillis(); 21 while ((d = src.read()) != -1) { 22 desc.write(d); 23 } 24 long end = System.currentTimeMillis(); 25 System.out.println("複製完畢!耗時:" + (end - start) + "ms"); // 複製完畢!耗時:2ms 26 } 27 }
通過上面的程式碼我們可以實現檔案的複製,但是這樣複製是比較耗時的,不如在電腦上直接複製貼上來的快。因為這是一個從硬碟讀取到記憶體中再寫到硬碟的過程,頻繁的一個位元組一個位元組的讀取頻繁地呼叫這個過程,所以會很慢。
如果我們不是一個位元組一個位元組讀寫,而是一組一組的讀寫就會提升效率,如下:
1 /** 2 * 若向提高讀寫效率, 3 * 可以通過提高每次讀寫的資料量來減少讀寫次數達到 4 * 5 * @author wjt 6 */ 7 public class RandomAccessFileDemo4 { 8 public static void main(String[] args) throws IOException { 9 RandomAccessFile src = new RandomAccessFile("raf.txt", "r"); 10 RandomAccessFile desc = new RandomAccessFile("raf1.txt", "rw"); 11 /** 12 * int read(byte[] data) 13 * 一次性嘗試讀取給定的位元組陣列總長度的位元組量並存入到該陣列中, 14 * 返回值為實際讀取到的位元組量, 15 * 若返回值為-1,則表示本次沒有讀取到任何資料(檔案末尾) 16 */ 17 // 10K 18 byte[] buf = new byte[1024 * 10]; 19 // 用來儲存讀取到每個位元組 20 int len = -1; 21 long start = System.currentTimeMillis(); 22 while ((len = src.read(buf)) != -1) { 23 /** 24 * void write(buf) 25 * 一次性將給定的位元組陣列中的所有資料寫入 26 * void write(byte[] d, int start, int end) 27 * 將所給定陣列中從小表start處開始的len個位元組一次性寫出 28 */ 29 desc.write(buf, 0, len); 30 } 31 long end = System.currentTimeMillis(); 32 System.out.println("複製完畢!耗時:" + (end - start) + "ms"); // 複製完畢!耗時:1ms 33 } 34 }
在上面的程式碼中,我們定義了一個 byte 陣列來每次讀寫 10K 的方式進行批量複製,會發現時間變短了,需要注意的是 write() 方法需要使用過載的方法,因為最後一次可能會寫入多餘的陣列len。
我們知道 write() 方法其實寫入的是資料的低八位,那我們想要寫入不同的資料型別該怎麼寫呢?如下程式碼:
1 /** 2 * RAF還提供了方便讀寫基本型別資料的方法 3 * 4 * @author wjt 5 */ 6 public class RandomAccessFileDemo5 { 7 public static void main(String[] args) throws IOException { 8 RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw"); 9 /** 10 * 向檔案中寫入一個int最大值 11 * Integer.MAX_VALUE 的二進位制位 12 * 011111111 11111111 11111111 11111111 13 * 檔案寫入為二進位制的低八位,如果我們直接寫入的話 14 * 其實寫入的是低八位的 11111111,顯然是錯誤的 15 * 我們可以通過位移的方式連續寫四次來進行寫入操作 16 */ 17 int max = Integer.MAX_VALUE; 18 19 raf.write(max >>> 24); // 01111111 20 raf.write(max >>> 16); // 11111111 21 raf.write(max >>> 8); // 11111111 22 raf.write(max); // 11111111 23 24 // RAF 其實提供了更方便的方法來進行不同型別資料的寫入 25 raf.writeInt(max); 26 raf.writeDouble(11.11); 27 raf.writeLong(1234L); 28 raf.close(); 29 } 30 }
從上面的程式碼中我們可以通過位移的方式按順序每次寫入一個int值的八位,寫四次正好是一個int值的四個位元組,當然我們我們也可以用提供好的 writeInt() 方法來直接寫入,其底層程式碼遠離其實也是用到了位移的思想,如下原始碼: