Java開發筆記(八十七)隨機訪問文件的讀寫

前面介紹了字符流讀寫文件的兩種方式,包括文件字符流和緩存字符流,可是它們的寫操做都存在一個問題:無論是write方法仍是append方法,都只能從文件開頭寫入,而不能追加到文件末尾或者在文件中間某個位置寫入。這個問題真很差辦,它意味着每次寫操做都會覆蓋掉原來的文件內容,注意是直接覆蓋而非局部修改,可大多數的業務場景須要在原文件基礎上追加或者修改的。假若堅持使用字符流修改文件內容,也不是不能夠,那樣得把原來的文件內容所有讀到某個字符串,再對該字符串進行修改操做,最後把改後的字符串從新寫入原文件。這麼處理的話,對付小文件倒還湊合,要是遇到超大文件,好比大小達到1G的文件,光光把這1G的數據讀到內存就足以讓程序崩潰了。所以,經過字符流修改文件並不是好辦法,不如採用專門的文件修改工具即RandomAccessFile(隨機訪問文件類),該工具特別適合對文件作各類花式修改。隨機文件工具RandomAccessFile提供了seek方法用來定位當前的讀寫位置,能夠很方便地在指定位置寫入數據,故而RandomAccessFile常常用於如下幾個場合:
一、往大文件末尾追加數據。
二、下載文件時候的斷點續傳,支持從上次已下載完成的地方中途開始,而沒必要重頭下載整個文件。
建立隨機文件對象依然要指定文件路徑,同時還要指定該文件的打開方式,下面是建立隨機文件對象的代碼例子:html

		// 根據文件路徑建立既可讀又可寫的隨機文件對象
		String mAppendFileName = "D:/test/random_appendStr.txt";
		RandomAccessFile raf = new RandomAccessFile(mAppendFileName, "rw");

 

上面構造方法的第二個參數值爲rw,表示以既可讀又可寫的模式打開文件。除了常見的rw,模式參數還有其它取值,具體的取值說明以下:
r:以只讀方式打開指定文件。若是試圖對該文件執行write寫入方法,則會拋出異常IOException。
rw:以可讀且可寫的方式打開指定文件。若是該文件不存在,則嘗試建立新文件。
rws:以可讀且可寫的方式打開指定文件。rws模式的每次write方法都會當即寫入文件,它至關於FileWriter;而rw模式先把數據寫到緩存,等到緩存滿了或者調用close方法關閉文件之時,纔將緩存中的數據真正寫入文件,它至關於BufferedWriter。
rwd:與rws模式相似。區別在於rwd只更新文件內容,不更新文件的元數據,而rws模式會同時更新文件內容及元數據。所謂元數據保存了文件的基本信息,包括文件類型(是文件仍是目錄)、文件的建立時間、文件的修改時間、文件的訪問權限(是否可讀、是否可寫、是否可執行)等等。
與字符流工具相比,隨機文件工具用起來反而更簡單,一個RandomAccessFile就集成了File、FileWriter、FileReader三個工具的基本用法,它的主要方法說明以下:
length:獲取指定文件的文件大小。
setLength:設置指定文件的文件大小。
seek:移動指定文件的訪問位置。
write:往文件的當前位置寫入字節數組。
read:把當前位置以後的文件內容讀到字節數組。
close:關閉文件。RandomAccessFile擁有close方法,意味着它支持try-with-resources方式的自動釋放資源。
以在文件末尾追加數據爲例,使用RandomAccessFile完成的話,先調用seek方法定位到文件末尾,再調用write方法寫入字節數組形式的數據。這個追加功能的實現代碼以下所示: java

	private static String mAppendFileName = "D:/test/random_appendStr.txt";
	// 往隨機文件末尾追加字符串
	private static void appendStr() {
		// 建立指定路徑的隨機文件對象(可讀寫)。try(...)支持在處理完畢後自動關閉隨機文件
		try (RandomAccessFile raf = new RandomAccessFile(mAppendFileName, "rw")) {
			long length = raf.length(); // 獲取隨機文件的長度(文件大小)
			raf.seek(length); // 定位到指定長度的位置
			String str = String.format("你好世界%.10f\n", Math.random());
			raf.write(str.getBytes()); // 往隨機文件寫入字節數組
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

從上面代碼看到,隨機文件工具可以直接往文件末尾添加數據,即便原文件有好幾個G大小,也絲絕不影響數據追加的效率。
再看一個往文件內部的任意位置插入數據的例子,仍然是先調用seek方法跳到指定位置,再調用write方法寫入字節數據。下面的演示代碼中,爲了確保seek跳轉的位置始終落在文件內部,在一開始就調用setLength方法設置文件的固定大小。在任意位置插入數據的詳細代碼參見以下:數組

	private static String mFixsizeFileName = "D:/test/random_fixsize.txt";
	// 往固定大小的隨機文件中插入數據
	private static void fixSizeInsert() {
		// 建立指定路徑的隨機文件對象(可讀寫)。try(...)支持在處理完畢後自動關閉隨機文件
		try (RandomAccessFile raf = new RandomAccessFile(mFixsizeFileName, "rw")) {
			raf.setLength(1000); // 設置隨機文件的長度(文件大小)
			for (int i=0; i<=2 ;i++) {
				raf.seek(i*200); // 定位到指定長度的位置
				String str = String.format("你好世界%.10f\n", Math.random());
				raf.write(str.getBytes()); // 往隨機文件寫入字節數組
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

最後瞧瞧隨機文件工具的讀文件操做,與字符流工具比較,它倆的處理流程大致一致,但在細節上有個區別:隨機文件工具的read方法支持一次性讀到字節數組,而字符流工具的read方法支持一次性讀到字符數組。下面是經過RandomAccessFile讀取文件內容的代碼例子,能夠看到它是以字節爲單位讀出數據的:緩存

	// 讀取隨機文件的文件內容
	private static void readContent() {
		// 建立指定路徑的隨機文件對象(只讀)。try(...)支持在處理完畢後自動關閉隨機文件
		try (RandomAccessFile raf = new RandomAccessFile(mAppendFileName, "r")) {
			int length = (int) raf.length(); // 獲取隨機文件的長度(文件大小)
			byte[] bytes = new byte[length]; // 分配長度爲文件大小的字節數組
			raf.read(bytes); // 把隨機文件的文件內容讀取到字節數組
			String content = new String(bytes); // 把字節數組轉成字符串
			System.out.println("content="+content);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

  


更多Java技術文章參見《Java開發筆記(序)章節目錄app

相關文章
相關標籤/搜索