三种访问文件的方式
标准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(避开内核缓冲区,适合在应用程序中管理缓存的系统,比如:数据库管理系统);
内存映射(磁盘文件与用户空间的直接映射,减少拷贝数据的次数,适合大文件的读写。);
阻塞与非阻塞,同步与异步的区分(非阻塞同步、非阻塞异步的介绍);
订阅号二维码: