標准IO,帶緩沖的標准IO,內存映射等在Java中的實現:
1 package com.mesopotamia.test; 2 3 import java.io.BufferedReader; 4 import java.io.ByteArrayInputStream; 5 import java.io.File; 6 import java.io.FileNotFoundException; 7 import java.io.FileReader; 8 import java.io.IOException; 9 import java.io.RandomAccessFile; 10 import java.nio.ByteBuffer; 11 import java.nio.MappedByteBuffer; 12 import java.nio.channels.FileChannel; 13 import java.util.Scanner; 14 15 import org.apache.log4j.Logger; 16 /* 17 * 原文學習請加微信訂閱號:it_pupil 18 * **/ 19 public class FileRead { 20 private static Logger logger = Logger.getLogger(FileRead.class); 21 public static void main(String args[]) throws FileNotFoundException{ 22 String path = "C:" + File.separator + "test" + File.separator + "Alice.txt"; 23 readFile3(path); 24 } 25 26 public static void readFile(String path) throws FileNotFoundException { 27 long start = System.currentTimeMillis();//開始時間 28 int bufSize = 1024;//1K緩沖區 29 File fin = new File(path); 30 /* 31 * 通道就是為操作文件而建立的一個連接。(讀寫文件、內存映射等) 32 * 此處的getChannel()可以獲取通道; 33 * 用FileChannel.open(filename)也可以創建一個通道。 34 * "r"表示只讀。 35 * 36 * RandomAccessFile是獨立與I/O流家族的類,其父類是Object。 37 * 該類因為有個指針可以挪動,所以,可以從任意位置開始讀取文件數據。 38 * **/ 39 FileChannel fcin = new RandomAccessFile(fin, "r").getChannel(); 40 //給字節緩沖區分配大小 41 ByteBuffer rBuffer = ByteBuffer.allocate(bufSize); 42 String enterStr = "\n"; 43 try { 44 byte[] bs = new byte[bufSize]; 45 String tempString = null; 46 while (fcin.read(rBuffer) != -1) {//每次讀1k到緩沖區 47 int rSize = rBuffer.position();//記錄緩沖區當前位置 48 rBuffer.rewind();//位置歸零,標記取消,方便下次循環重新讀入緩沖區。 49 rBuffer.get(bs);//將緩沖區數據讀到字節數組中 50 rBuffer.clear();//清除緩沖 51 /* 52 * 用默認編碼將指定字節數組的數據構造成一個字符串 53 * bs:指定的字節數組,0:數組起始位置;rSize:數組結束位置 54 * */ 55 tempString = new String(bs, 0, rSize); 56 int fromIndex = 0;//每次讀的開始位置 57 int endIndex = 0;//每次讀的結束位置 58 //按行讀String數據 59 while ((endIndex = tempString.indexOf(enterStr, fromIndex)) != -1) { 60 String line = tempString.substring(fromIndex, endIndex);//轉換一行 61 System.out.print(line); 62 fromIndex = endIndex + 1; 63 } 64 } 65 long end = System.currentTimeMillis();//結束時間 66 System.out.println("傳統IO讀取數據,指定緩沖區大小,總共耗時:"+(end - start)+"ms"); 67 68 } catch (IOException e) { 69 e.printStackTrace(); 70 } 71 } 72 73 public static void readFile1(String path) { 74 long start = System.currentTimeMillis();//開始時間 75 File file = new File(path); 76 if (file.isFile()) { 77 /*使用Reader家族,表示我要讀字符數據了, 78 *使用該家族中的BufferedReader,表示我要建立緩沖區讀字符數據了。 79 * */ 80 BufferedReader bufferedReader = null; 81 FileReader fileReader = null; 82 try { 83 fileReader = new FileReader(file); 84 //嵌套使用,裝飾者模式,老生常談。裝飾者模式的使用,可以讀前面小磚寫的《從熏肉大餅到裝飾者模式》 85 bufferedReader = new BufferedReader(fileReader); 86 String line = bufferedReader.readLine(); 87 //一行一行讀 88 while (line != null) { //按行讀數據 89 System.out.println(line); 90 line = bufferedReader.readLine(); 91 } 92 } catch (FileNotFoundException e) { 93 e.printStackTrace(); 94 } catch (IOException e) { 95 e.printStackTrace(); 96 } finally { 97 //最後一定要關閉 98 try { 99 fileReader.close(); 100 bufferedReader.close(); 101 } catch (IOException e) { 102 e.printStackTrace(); 103 } 104 long end = System.currentTimeMillis();//結束時間 105 System.out.println("傳統IO讀取數據,不指定緩沖區大小,總共耗時:"+(end - start)+"ms"); 106 } 107 108 } 109 } 110 111 public static void readFile3(String path) { 112 long start = System.currentTimeMillis();//開始時間 113 long fileLength = 0; 114 final int BUFFER_SIZE = 0x300000;// 3M的緩沖 115 File file = new File(path); 116 fileLength = file.length(); 117 try { 118 /*使用FileChannel.map方法直接把整個fileLength大小的文件映射到內存中**/ 119 MappedByteBuffer inputBuffer = new RandomAccessFile(file, "r").getChannel() 120 .map(FileChannel.MapMode.READ_ONLY, 0, fileLength);// 讀取大文件 121 byte[] dst = new byte[BUFFER_SIZE];// 每次讀出3M的內容 122 //每3M做一個循環,分段將inputBuffer的數據取出。 123 for (int offset = 0; offset < fileLength; offset += BUFFER_SIZE) { 124 //防止最後一段不夠3M 125 if (fileLength - offset >= BUFFER_SIZE) { 126 //一個字節一個字節的取出來放到byte[]數組中。 127 for (int i = 0; i < BUFFER_SIZE; i++) 128 dst[i] = inputBuffer.get(offset + i); 129 } else { 130 for (int i = 0; i < fileLength - offset; i++) 131 dst[i] = inputBuffer.get(offset + i); 132 } 133 // 將得到的3M內容給Scanner,這裡的XXX是指Scanner解析的分隔符。 134 Scanner scan = new Scanner(new ByteArrayInputStream(dst)).useDelimiter("XXX"); 135 //hasNext()所參照的token就是上面的XXX 136 while (scan.hasNext()) { 137 // 這裡為對讀取文本解析的方法 138 System.out.print(scan.next() + "XXX"); 139 } 140 scan.close(); 141 } 142 System.out.println(); 143 long end = System.currentTimeMillis();//結束時間 144 System.out.println("NIO 內存映射讀大文件,總共耗時:"+(end - start)+"ms"); 145 } catch (Exception e) { 146 e.printStackTrace(); 147 } 148 } 149 }
內容來自:《磁盤IO的工作機制》,發布自微信訂閱號:it_pupil
《磁盤IO的工作機制》主要內容:
內核空間與用戶空間(以及數據在二者之間的轉換);
標准訪問文件的方式(要同時經過內核空間、用戶空間的雙重拷貝)(局部性原理);
帶緩沖訪問文件的方式;
直接IO(避開內核緩沖區,適合在應用程序中管理緩存的系統,比如:數據庫管理系統);
內存映射(磁盤文件與用戶空間的直接映射,減少拷貝數據的次數,適合大文件的讀寫。);
阻塞與非阻塞,同步與異步的區分(非阻塞同步、非阻塞異步的介紹);
訂閱號二維碼: