1. 程式人生 > >Java開發筆記(八十七)隨機訪問文件的讀寫

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

sta 世界 ant 處理流程 txt www cep 滿了 字節數組

前面介紹了字符流讀寫文件的兩種方式,包括文件字符流和緩存字符流,但是它們的寫操作都存在一個問題:不管是write方法還是append方法,都只能從文件開頭寫入,而不能追加到文件末尾或者在文件中間某個位置寫入。這個問題真不好辦,它意味著每次寫操作都會覆蓋掉原來的文件內容,註意是直接覆蓋而非局部修改,可大多數的業務場景需要在原文件基礎上追加或者修改的。倘若堅持使用字符流修改文件內容,也不是不可以,那樣得把原來的文件內容全部讀到某個字符串,再對該字符串進行修改操作,最後把改後的字符串重新寫入原文件。這麽處理的話,對付小文件倒還湊合,要是遇到超大文件,比如大小達到1G的文件,光光把這1G的數據讀到內存就足以讓程序崩潰了。因此,通過字符流修改文件並非好辦法,不如采用專門的文件修改工具即RandomAccessFile(隨機訪問文件類),該工具特別適合對文件做各種花式修改。隨機文件工具RandomAccessFile提供了seek方法用來定位當前的讀寫位置,可以很方便地在指定位置寫入數據,故而RandomAccessFile經常用於以下幾個場合:

1、往大文件末尾追加數據。
2、下載文件時候的斷點續傳,支持從上次已下載完成的地方中途開始,而不必重頭下載整個文件。
創建隨機文件對象依然要指定文件路徑,同時還要指定該文件的打開方式,下面是創建隨機文件對象的代碼例子:

		// 根據文件路徑創建既可讀又可寫的隨機文件對象
		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方法寫入字節數組形式的數據。這個追加功能的實現代碼如下所示:

	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開發筆記(序)章節目錄》

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