在之前的文章我們已經可以對本地對文件和目錄進行新建和刪除等操作,接下來我們來對文件內對具體內容進行操作。
如下代碼,我們實現了一個基本的文件寫入:
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() 方法來直接寫入,其底層代碼遠離其實也是用到了位移的思想,如下源碼: