1. 程式人生 > >Java開發筆記(九十五)NIO配套的文件工具Files

Java開發筆記(九十五)NIO配套的文件工具Files

存在 進行 trac spa pan write tps 選項參數 println

NIO不但引進了高效的文件通道,而且新增了更加好用的文件工具家族,包括路徑組工具Paths、路徑工具Path、文件組工具Files。先看路徑組工具Paths,該工具提供了靜態方法get,輸入某個文件的路徑字符串,輸出該文件路徑的路徑對象Path。通過get方法獲取路徑對象的代碼示例如下:

		// 根據指定的文件路徑字符串獲得對應的Path對象
		Path path = Paths.get(mDirName);

有了Path對象之後,就能調用它的各種實例方法了,常見的幾個方法說明如下:
getParent:獲取當前路徑所在的上級目錄的Path對象。
resolve:拼接文件路徑,在當前路徑的末尾添加指定字符串,並返回新的文件路徑。

startsWith:判斷當前路徑是否以指定字符串開頭。
endsWith:判斷當前路徑是否以指定字符串結尾。
toString:獲取當前路徑對應的名稱字符串。
toFile:獲取當前路徑對應的File對象。
看上去路徑組工具Paths和路徑工具Path平淡無奇,並無什麽出眾之處。原來真正方便的是文件組工具Files,它集成了眾多實用的功能技巧,且看下列的各個方法說明:
exists:判斷該路徑是否存在。
isDirectory:判斷該路徑是否為目錄。
isExecutable:判斷該路徑是否允許執行。
isHidden:判斷該路徑是否隱藏。
isReadable:判斷該路徑是否可讀。
isWritable:判斷該路徑是否可寫。
size:獲取該路徑的文件大小。如果該路徑是文件,則返回文件大小;如果該路徑是目錄,則返回目錄基本信息的大小,而非整個目錄的大小。
createDirectory:如果該路徑是個目錄,就創建新目錄。
createFile:如果該路徑是個文件,就創建新文件。
delete:如果該路徑是文件或者空目錄,就把它刪掉。如果該路徑不存在或者目錄非空,就扔出異常。
deleteIfExists:如果該路徑是文件或者空目錄,就把它刪掉(路徑不存在也不報錯)。但若目錄非空,還是扔出異常。
copy:把文件從源路徑復制到目標路徑。
move:把文件從源路徑移動到目標路徑。
另外,Java8又給Files工具增加了以下幾個方法,使之具備流式處理的能力:
readAllLines:獲取該文件的所有內容行,返回的是字符串清單。
lines:獲取該文件的所有內容行,返回的是字符串流Stream<String>。
list:獲取該目錄下的所有文件與目錄,但不包括子目錄的下級內容,返回的是路徑流Stream<Path>。
walk:獲取該目錄下的所有文件與目錄,且包括指定深度子目錄的下級內容,返回的是路徑流Stream<Path>。

接下來通過幾個實際案例演示以上文件工具的詳細用法。

一、通過Path打開文件通道
之前介紹文件通道的時候,提到有兩種方式可以創建文件通道,第一種方式可以調用輸入輸出流的getChannel方法獲取通道對象,第二種方式可以調用隨機文件工具的getChannel方法獲取通道對象。其實還有第三種方式,就是調用FileChannel工具的open方法,根據傳入的Path對象也能獲得通道對象。不加選項參數的open方法,默認得到只讀的文件通道;若要得到可寫的文件通道,則需給open方法傳入選項參數StandardOpenOption.WRITE。下面是利用路徑工具創建文件通道的代碼例子:

	// 通過Path打開文件通道
	private static void openChannelFromPath() {
		try {
			// 根據指定的文件路徑字符串獲得對應的Path對象
			Path path = Paths.get(mFileName);
			// 創建文件通道的第三種方式:通過Path打開文件的只讀通道。open方法不加選項參數的話,默認是只讀權限
			FileChannel readChannel = FileChannel.open(path, StandardOpenOption.READ);
			readChannel.close();
			// 創建文件通道的第三種方式:通過Path打開文件的寫入通道。
			// open方法的第二個參數指定了文件以只讀方式還是以可寫方式打開
			FileChannel writeChannel = FileChannel.open(path, StandardOpenOption.WRITE);
			writeChannel.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

需要註意的是,通過Path打開可寫的文件通道有個問題:要是文件通道指向的文件路徑並不存在,那麽往該通道寫入數據將會扔出異常,而非默認創建新文件。因而獲取可寫的文件通道之前必須添加檢查代碼,即判斷指定路徑是否存在,倘若該路徑不存在,則要創建一個新文件。完整的檢查代碼如下所示:

	// 根據文件路徑獲取Path對象。如果指定路徑的文件不存在,就創建一個新文件。
	private static Path getPath(String filename) {
		Path path = Paths.get(filename);
		if (!Files.exists(path)) { // 該文件路徑並不存在
			try {
				Files.createFile(path); // 在該路徑創建新文件
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return path;
	}

可是依稀記得,不管是從輸入輸出流獲取文件通道,還是從隨機文件工具獲取文件通道,都沒有手工創建新文件的步驟呀。那是因為即使指定路徑的文件不存在,輸出流和隨機文件工具都會自動創建文件,無需程序員去手工創建。因此實際開發中若要創建文件通道,基本采取前兩種方式,很少使用Path工具的第三種方式。

二、遍歷指定目錄下(不包含子目錄)的所有文件與目錄
調用Files工具的list方法即可實現指定目錄(不包含子目錄)的遍歷功能,list方法返回的遍歷結果為字符串流,後續即可通過流式處理做進一步的加工。比如要統計指定目錄下面文件與目錄數量,則先調用list方法獲得字符串流對象,再調用count方法就能得到統計數目。具體的統計代碼示例如下:

		// 根據指定的文件路徑字符串獲得對應的Path對象
		Path path = Paths.get(mDirName);
		try {
			// 計算該目錄下(不包含子目錄)的所有文件與目錄的總數
			long listCount = Files.list(path).count();
			System.out.println("listCount="+listCount);
		} catch (Exception e) {
			e.printStackTrace();
		}

  

三、遍歷指定目錄下(包含子目錄)的所有文件與目錄
倘若要求對指定目錄及其子目錄進行遍歷操作,則可調用Files工具的walk方法,該方法支持設定待遍歷的子目錄深度(從當前目錄往下數的目錄層數)。譬如要統計指定目錄及五層以內子目錄下面文件與目錄數量,則先調用walk方法獲得字符串流對象,再調用count方法就能得到統計數目。此時包含子目錄的統計代碼如下所示:

		try {
			// 根據指定的文件路徑字符串獲得對應的Path對象
			Path path = Paths.get(mDirName);
			// 遍歷該目錄以及深度在五之內的子目錄,計算其下所有文件與目錄的總數
			long count = Files.walk(path, 5).count();
			System.out.println("count="+count);
		} catch (Exception e) {
			e.printStackTrace();
		}

walk方法與list方法同樣返回的都是流對象,所以流式處理的filter、map、collect等方法統統適用,非常方便對某目錄下的所有實體進行篩選操作。例如打算遍歷指定目錄以及深度在五之內的子目錄,並返回其下所有目錄的路徑名稱清單,利用walk方法實現的篩選代碼是下面這樣的:

		try {
			// 根據指定的文件路徑字符串獲得對應的Path對象
			Path path = Paths.get(mDirName);
			// 遍歷該目錄以及深度在五之內的子目錄,並返回其下所有目錄的路徑名稱清單
			List<String> dirs = Files.walk(path, 5)
					.filter(Files::isDirectory) // 只挑選目錄
					.map(it -> it.toString()) // 獲取目錄的路徑名稱
					.collect(Collectors.toList()); // 返回清單格式
			System.out.println("dirs="+dirs);
		} catch (Exception e) {
			e.printStackTrace();
		}

由此可見,流式處理在NIO的文件工具中大放異彩,代碼邏輯結構清晰、代碼行數量也少,實為文件遍歷的一員福將。
通過walk方法篩選指定目錄下的某種類型文件也很方便,例如想要挑出某目錄下面所有的png圖片文件路徑,則采取walk方法輔以流式處理的實現代碼如下所示:

		try {
			// 根據指定的文件路徑字符串獲得對應的Path對象
			Path path = Paths.get(mDirName);
			// 遍歷該目錄以及深度在五之內的子目錄,並返回其下所有png文件的路徑名稱清單
			List<String> pngs = Files.walk(path, 5)
					.filter(it -> it.toFile().isFile()) // 只挑選文件
					.filter(it -> it.endsWith(".png")) // 挑出擴展名為png的文件
					.map(it -> it.toString()) // 獲取目錄的路徑名稱
					.collect(Collectors.toList()); // 返回清單格式
			System.out.println("pngs="+pngs);
		} catch (Exception e) {
			e.printStackTrace();
		}

以上的文件篩選代碼果然清爽,一點都不拖泥帶水。



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

Java開發筆記(九十五)NIO配套的文件工具Files