【转】Java中的IO操作
在使用io操作之前,先看一下java中的文件类File如何使用。File包括文件和目录,对文件和目录的操作是新建目录mkdir,新建文件createNewFile,删除文件和目录delete,以及其他的一些操作。
package zaojiahua; import java.io.File; import java.io.IOException; /* 对文件类的操作 */ public class Test { public static void main(String[] args) throws IOException { //新建一个目录 File file = new File("1/2"); //判断目录是否存在 if(!file.exists()) { //创建多层目录 file.mkdirs(); } //创建文件 File file2 = new File(file,"1.txt"); file2.createNewFile(); //创建文件 File file3 = new File("2.txt"); file3.createNewFile(); //获取文件的绝对路径 System.out.println(file3.getAbsolutePath()); System.out.println(file2.getParent()); //删除目录 file.delete(); File file4 = new File("1/3"); //遍历目录中的文件和目录,是单层遍历 String str[] = file4.list(); for(String s : str) { System.out.println(s); } //列出一个目录下的所有文件 System.out.println("**********************listAll**********************"); listAll(file4); } static void listAll(File filename) { if(filename.isDirectory()) { File fileDir [] = filename.listFiles(); for(File file : fileDir) { if(file.isDirectory()) { System.out.println("目录:"+file); listAll(file); } else System.out.println("文件:"+file); } } else { System.out.println("文件:"+filename); } } }
接下来是java中IO类的操作,IO分类如下图所示,这里只说几个常用的。
首先是FileInputStream和FileOutputStream,这一对流是对文件进行读写的,读写方法是从父类inputStream和OutputStream继承下来的read和write方法。
package com.zaojiahua.iodemo; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Test { public static void main(String[] args) throws IOException { File file = new File("file.txt"); //实现数据的拷贝 FileInputStream inputStream = new FileInputStream(file); FileOutputStream outputStream = new FileOutputStream("1.txt"); // int i; // while((i = inputStream.read() ) != -1) // { // //read的返回值是int,如果遇到了文件尾则返回-1 // outputStream.write(i); // } //或者使用如下的方法 byte [] buf = new byte[1024]; int len = -1; while((len = inputStream.read(buf)) != -1) { outputStream.write(buf); } inputStream.close(); outputStream.close(); } }
BufferedInputStream对外提供滑动读取的功能实现,通过预先读入一整段原始输入流数据至缓冲区中,而外界对BufferedInputStream的读取操作实际上是在缓冲区上进行,如果读取的数据超过了缓冲区的范围,那么BufferedInputStream负责重新从原始输入流中载入下一截数据填充缓冲区,然后外界继续通过缓冲区进行数据读取。这样的设计的好处是:避免了大量的磁盘IO,因为原始的InputStream类实现的read是即时读取的,即每一次读取都会是一次磁盘IO操作(哪怕只读取了1个字节的数据),可想而知,如果数据量巨大,这样的磁盘消耗非常可怕。而通过缓冲区的实现,读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进行一次磁盘IO,载入一段数据填充缓冲,那么下一次读取一般情况下就直接可以从缓冲区读取,减少了磁盘IO。说白了buffered就是用来缓存的,可以用来提高读取的效率,之所以说FileInputStream是阻塞的方法是因为CUP的速度和磁盘的速度是不匹配的,如果每次要读取的时候都访问磁盘这样就造成了阻塞。通过以上的说明可以看出buffered则并不是阻塞的。所以我们读取文件的时候一般都在文件流上边套上一层buffer流。
package com.zaojiahua.iodemo; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Test { public static void main(String[] args) throws IOException { File file = new File("file.txt"); //使用buffer实现数据的拷贝 BufferedInputStream input = new BufferedInputStream(new FileInputStream(file)); BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream("1.txt")); //或者使用如下的方法 byte [] buf = new byte[1024]; int len = -1; while((len = input.read(buf)) != -1) { output.write(buf,0,len); //将读取到的字节数据转化为字符串打印出来 String str = new String(buf,0,len); System.out.println(str); } //关闭流 input.close(); output.close(); } }
数据在硬盘上都是以字节的方式存储的,那么什么时候使用字符流,什么时候使用字节流呢?字符流是对字符操作的,也就是对文本文件或者其他字符文件操作。而对于图片,声音,视频这些文件则用字节流操作。接下来是对字符流的操作,字符流相当于是字节流+编码表。可以向文件中直接写入字符串。读取的时候注意是用字符数组接受,不是字节数组。
package com.zaojiahua.iodemo; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Test { public static void main(String[] args) throws IOException { File file = new File("file.txt"); //使用字符流实现文本文件的复制 FileReader reader = new FileReader(file); FileWriter writer = new FileWriter("2.txt"); int len; //这里是字符数组,不是字节数组 char [] buf = new char[1024]; while((len = reader.read(buf)) != -1) { System.out.println(new String(buf,0,len)); writer.write(buf,0,len); } //可以将字符串直接写到文件中 writer.write("\r\n"+"end"); //关闭流 reader.close(); writer.close(); } }
以下是缓冲的字符流。经过缓冲以后可以读取和写入一行的数据。
package com.zaojiahua.iodemo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Test { public static void main(String[] args) throws IOException { File file = new File("file.txt"); //使用字符流实现文本文件的复制 BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("2.txt")); String buf; //使用buffer缓冲的时候可以读取和写入一行 while((buf = bufferedReader.readLine()) != null) { bufferedWriter.write(buf); //写入一行 bufferedWriter.newLine(); System.out.println(buf); } //关闭流 bufferedReader.close(); bufferedWriter.close(); } }
最后解决的一个问题是字节流和字符流的转化,使用的是InputStreamReader和OutputStreamWriter,它们本身属于的是reader和writer字符流,我们之所以会用到这些转化流是因为系统有时候只给我们提供了字节流,为了方便操作,要用到字符流。比如说System.in标准输入流就是字节流。你想从那里得到用户在键盘上的输入,只能是以转换流将它转换为Reader以方便自己的程序读取输入。再比如说Socket里的getInputStream()很明显只给你提供字节流,你要想读取字符,就得给他套个InputStreamReader()用来读取。
package com.zaojiahua.iodemo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class Test { public static void main(String[] args) throws IOException { //字节流和字符流的相互转化 FileInputStream fileInputStream = new FileInputStream("input.txt"); //inputSreamReader本来就是reader对象,创建的时候需要传入一个InputStream对象,将字节流转化为字符流 BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); //将字符流转化为字节流 OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("output.txt")); BufferedWriter writer = new BufferedWriter(outputStreamWriter); //实现拷贝文件的操作 String buf; while((buf = reader.readLine()) != null) { writer.write(buf); writer.newLine(); System.out.println(buf); } //关闭流 reader.close(); writer.close(); } }