SE高階(3):NIO流—使用NIO執行讀/寫操作代碼實例

代碼演示Buffer

		//創建字節緩衝區,容量1024
		ByteBuffer buff = ByteBuffer.allocate(1024);
		System.out.println(buff.position());//讀寫起始點
		System.out.println(buff.limit());//界限位置
		//字節緩衝區放入3個int數值
		buff.putInt(10);
		buff.putInt(15);
		buff.putInt(20);
		System.out.println(buff.position());// 放入數據後,position:12
		//切換讀取模式,方便輸出數據
		buff.flip();
		System.out.println("切換寫模式後,position———limit," + buff.position() + "———" + buff.limit());
		//切換寫入模式,方便獲取數據
		buff.clear();
		System.out.println("切換讀模式後,position———limit," + buff.position() + "———" + buff.limit());
上面代碼中,Buffer的allocate(int capacity)用於創建緩衝區。position()與limit()分別獲取當前緩衝區的讀寫點與終止點。使用put()放入數據,put()有一系列方法放入不同數類型據。因爲是字節緩衝區,而int數值是佔4字節,所以position是12。flip()和clear()使用之後,會對讀寫點和終止點進行改變。當然,還有一系列方法,可自己實驗。

創建Channel

		File f1 = new File("D:\\reviewIO\\ChannelDemo.txt");
		File f2 = new File("D:\\reviewIO\\ChannelDemo2.txt");

		//文件輸入/輸出流來創建通道
		FileInputStream fis = new FileInputStream(f1);
		FileOutputStream fos = new FileOutputStream(f2);
		FileChannel inChannel = fis.getChannel();//讀取通道
		FileChannel outChannel = fos.getChannel();//寫入通道
		//使用RandomAccessFile來創建FileChannel
		FileChannel rafChannel = new RandomAccessFile(f1,"rw").getChannel();
		FileChannel rafChannel2 = new RandomAccessFile(f2,"rw").getChannel();
上面代碼中,使用輸入/輸出流來分別創建了文件通道,雖然通道是雙向的。但輸入流的通道只能用於讀取數據到緩衝區,輸出流的通道用於把緩衝區數據寫入通道。使用RandomAccessFile類也可以創建通道,RandomAccessFile可以設爲讀寫模式。

使用Channel的內存映射

		File f1 = new File("D:\\reviewIO\\ChannelDemoF1.txt");
		File f2 = new File("D:\\reviewIO\\ChannelDemoF2.txt");
		//使用RandomAccessFile來創建FileChannel
		FileChannel inChannel = new RandomAccessFile(f1,"rw").getChannel();
		FileChannel outChannel = new RandomAccessFile(f2,"rw").getChannel();
		
		//把rafChannel通道的全部數據映射成ByteBuffer
		MappedByteBuffer mapBuff = inChannel.map(MapMode.READ_ONLY, 0, f1.length());
上面代碼中,Channel直接把通道中全部數據映射成ByteBuffer,使用內存映射可以大幅度提高文件拷貝性能,後面會進行比較。也可以使用Buffer的allocate()來自由分配緩衝區容量,看個人需求來。

使用NIO讀取文件數據

		File file = new File("D:\\reviewIO\\word.txt");
		//以只讀模式來創建通道
		FileChannel inChannel = new RandomAccessFile(file,"r").getChannel();
		//創建字節緩衝區
		ByteBuffer bytebuf = ByteBuffer.allocate(1024);	
		//默認字符集創建解碼器
		CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
		
		while((inChannel.read(bytebuf)) != -1) {//讀取通道數據到緩衝區中,非-1就代表有數據
			//確定緩衝區數據的起點和終點
			bytebuf.flip();
			//對bytebuf進行解碼,避免亂碼
			CharBuffer decode = decoder.decode(bytebuf);
			System.out.println(decode.toString());
			//清空緩衝區,再次放入數據
			bytebuf.clear();
		}	
使用nio流讀取文件要注意flip()和clear()方法,flip()是確定緩衝區數據的起點和終點位置,避免訪問到limit()之後的區域。clear()是把緩衝區初始化,方便再次寫入數據到緩衝區。因爲是ByteBuffer,所以無法直接顯示文本內容,需要使用解碼器來轉碼成字符緩衝區。

nio流寫入字符串到文件中

		Scanner sc = new Scanner(System.in);
		File file = new File("D:\\reviewIO\\writeNio.java");
		if(!file.exists()) 
			file.createNewFile();
		//RandomAccessFile創建通道,是讀寫模式
		FileChannel inChannel = new RandomAccessFile(file,"rw").getChannel();
		//創建字符緩衝區,容量1024
		CharBuffer charBuf = CharBuffer.allocate(1024);	
		//使用默認字符集創建編碼器
		CharsetEncoder encoder = Charset.defaultCharset().newEncoder();
		String str = null;
		while(!(str = sc.next()).equals("end")) {
			//往緩衝區中寫入數據
			charBuf.put(str+"\r\n");
			//爲輸出緩衝區數據做準備
			charBuf.flip();
			//對緩衝區進行編碼,避免亂碼
			ByteBuffer bb = encoder.encode(charBuf);
			//把緩衝區數據寫入通道
			inChannel.write(bb);
			//Buffer初始化,爲下一次讀取數據做準備
			charBuf.clear();
		}		
上面代碼實現從鍵盤輸入數據,然後寫入指定文件中。因爲緩衝區是字符類型,而通道的讀寫操作都是使用byteBuffer,所以要使用編碼器將CharBuffer轉碼成ByteBuffer,才能往通道中寫入數據。

使用內存映射拷貝文件

	public static void copyLargeFile(String srcPath, String destPath) throws IOException {
		File src = new File(srcPath);//源文件
		File dest = new File(destPath);//拷貝的文件
		FileChannel fcin = null;//文件輸入通道
		FileChannel fcout = null;//文件輸出通道
		if(!src.isFile()) {
			 System.err.println("源路徑指向的不是文件");
			 return;
		}
		if(!src.exists() || !dest.exists()) {
			System.err.println("源文件或者拷貝文件路徑不存在,請檢查!");
			return;
		}
		fcin = new FileInputStream(src).getChannel();
		fcout = new FileOutputStream(dest).getChannel();
		//把文件輸入通道數據全部映射成ByteBuffer
		MappedByteBuffer buf = fcin.map(FileChannel.MapMode.READ_ONLY, 0, fcin.size());				
		//fcout寫入數據,數據源是buf緩衝區
		fcout.write(buf);
		//buf初始化,準備再次接收數據
		buf.clear();
	}
NIO拷貝大型文件性能很好,如果是拷貝小文件那和IO流差別不大。所以那個體系用得爽就用那個。

自定義緩衝區分批次拷貝文件

	public static void copyLargeFile2(String srcPath, String destPath) throws IOException {
		File src = new File(srcPath);//源文件
		File dest = new File(destPath);//拷貝文件路徑
		FileChannel fcin = null;//文件輸入通道
		FileChannel fcout = null;//文件輸出通道
		if(!src.isFile()) {
			 System.err.println("源路徑指向的不是文件");
			 return;
		}
		if(!src.exists() || !dest.exists()) {
			System.err.println("源文件或者拷貝文件路徑不存在,請檢查!");
			return;
		}
		fcin = new FileInputStream(src).getChannel();
		fcout = new FileOutputStream(dest).getChannel();
		//容量1024緩衝區
		ByteBuffer buf = ByteBuffer.allocate(1024);
		while((fcin.read(buf)) != -1) {//讀取管道數據到緩衝區中,爲1則結束
			//確定緩衝區數據的起點和終點
			buf.flip();
			//fcout寫入數據,數據源是buf緩衝區
			fcout.write(buf);
			//buf初始化,準備再次接收數據
			buf.clear();
		}		
	}

使用自定義緩衝區可以避免一次性寫入大量數據到內存中。還有如果拷貝的是文本數據,也建議使用ByteBuffer。不要使用CharBuffer作爲緩衝區,省略編碼步驟。如果是讀取文本數據並且顯示的話還是需要解碼器轉碼成字符緩衝區。

發佈了61 篇原創文章 · 獲贊 24 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章