標准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(避開內核緩沖區,適合在應用程序中管理緩存的系統,比如:數據庫管理系統);
內存映射(磁盤文件與用戶空間的直接映射,減少拷貝數據的次數,適合大文件的讀寫。);
阻塞與非阻塞,同步與異步的區分(非阻塞同步、非阻塞異步的介紹);
訂閱號二維碼:
