你的位置:首页 > Java教程

[Java教程]三种访问文件的方式


标准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           //防止最后一段不够3M125           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就是上面的XXX136           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(避开内核缓冲区,适合在应用程序中管理缓存的系统,比如:数据库管理系统);

  内存映射(磁盘文件与用户空间的直接映射,减少拷贝数据的次数,适合大文件的读写。);

  阻塞与非阻塞,同步与异步的区分(非阻塞同步、非阻塞异步的介绍);