读Java编程艺术之笔记(文件IO)(一)
数据流指具有一定字节长度和方向的线性有序的数据对象。
在Unix/Linux中,路径中的大、小写字母表示不同的路径;而Windows操作系统则忽略大小写。PS. File类的两个常量:File.separator在UNIX系统中值是/,在Windows系统中值是\\;File.pathSeparator在UNIX系统中值是:,在Windows系统中值是;。
Java流操作有关的类和接口:(这里的讨论转自他处,可惜笔记时未记出处)
File类,对文件系统中文件及文件夹进行封装的对象。File类保存文件或目录的各种元数据信息,包括文件名、文件长度、最后修改时间、是否可读。File类还拥有获取当前文件的路径名,判断指定文件是否存在,获得当前目录中的文件列表,创建、删除文件和目录的方法。
RandomAccessFile类封装了字节流,同时也封装了一缓冲区(字符数组)通过内部的指针来操作字符数组中的数据,只操作文件。构造函数接收两种类型:文件路径(字符串形式)和File对象。进行RandomAccessFile对象实例化时,可指定操作模式(如r、rw),实例化时如果操作的文件不存在,会自动创建该文件;若文件存在,而写数据未指定位置,则会从头开始写,即覆盖原有的内容。
字节流也称为原始数据,需要用户读入后做相应的转换,由InputStream、OutputStream及其子类处理;字符流的实现基于自动转换,读取数据时按照JVM默认编码自动转换成字符,由Writer、Reader及其子类处理。字节流与字符流的区别:读写单位不同,字节(Byte),字符(两字节的Unicode);处理对象不同,字节流可以处理所有数据类型(如图片等),读取的是机器存储时的字节,而字符流会由JVM将字节转化为Unicode字符,所以只能处理字符类型,对文本支持比较好。PS. InputStream,OutputStream,Reader,writer都是抽象类,所以不能直接new。
关于字节流与字符流区别的进一步讨论:详细出处参考http://blog.csdn.net/cynhafa/article/details/6882061
字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件。通过两个例子来说明:
使用字节流不关闭执行
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class OutputWriter { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 第1步:使用File类找到一个文件 File f = new File("d:" + File.separator + "test.txt"); // 声明File 对象 // 第2步:通过子类实例化父类对象 OutputStream out = null; // 准备好一个输出的对象 out = new FileOutputStream(f); // 通过对象多态性进行实例化 // 第3步:进行写操作 String str = "Hello World!!!"; // 准备一个字符串 byte b[] = str.getBytes(); // 字符串转byte数组 out.write(b); // 将内容输出 // 第4步:关闭输出流 // out.close(); // 此时没有关闭 } }
此时test文件中有内容为Hello World!!!
使用字符流不关闭执行
import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; public class OutputWriter { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 第1步:使用File类找到一个文件 File f = new File("d:" + File.separator + "test.txt");// 声明File 对象 // 第2步:通过子类实例化父类对象 Writer out = null; // 准备好一个输出的对象 out = new FileWriter(f); // 通过对象多态性进行实例化 // 第3步:进行写操作 String str = "Hello World!!!"; // 准备一个字符串 out.write(str); // 将内容输出 // 第4步:关闭输出流 // out.close(); // 此时没有关闭 } }
此时查看test文件,没有任何内容。
字符流操作时使用了缓冲区,而在关闭字符流时会强制性地将缓冲区中的内容进行输出,但是如果程序没有关闭,程序中也没有关闭字符流,则缓冲区中的内容是无法输出的。如果想在不关闭时也可以将字符流的内容全部输出,则可以使用Writer类中的flush()方法完成。
在从字节流转化为字符流时,实际上就是byte[]转化为String的过程,方法public String(byte bytes[], String charsetName),参数中有一个关键的参数字符集编码,通常我们都省略了,系统就用操作系统的lang;而在字符流转化为字节流时,实际上是String转化为byte[]时,方法byte[] String.getBytes(String charsetName),也是同样的道理。
文件操作中的缓冲,指一段指定的内存,用来暂存文件IO数据流中的数据,应用缓冲的目的是提高代码中频繁进行数据写入或读出的效率。当满足如下任何一个条件时,缓冲器的数据流依次批处理程序读入或写出到输出设备:缓冲器满,关闭文件(调用close()方法),刷新缓冲(调用flush()方法)。
文件IO异常是检查性异常,即代码中必须提供处理文件IO异常的机制,即利用try-catch程序块处理异常,或利用throw传播异常。文件IO包括如下可能发生的异常:IOException——处理IO出错时抛出的异常;EOFException——程序试图读取超出文件范围的数据时抛出的异常;FileNotFoundException——程序试图打开一个不存在文件时抛出的异常。IOException是EOFException和FileNotFoundException的超类,而其超类是Exception,直至Throwable。
虽然文本文件与二进制文件相比,占据更多内存,但文本文件应用对字符串为主的数据流的IO处理,其好处是直观和易于操作。Java.io包中提供以Writer和Reader为超类的两组API类来进行文本文件的IO处理和操作。
在具体文本输出代码中,按照从文件到缓冲,再到打印文本文件次序编写。如:
PrinterWriter
-->BufferedWriter (可选项)
-->FIleWriter
如果无需应用缓冲,则可利用PrintWriter(File file)直接创建文件对象,进行文本文件的输出。
import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class OutputWriter { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("D:" + File.separator + "test.txt"); // java.io.FileWriter.FileWriter(File file, boolean append) throws IOException // append if true, then bytes will be written to the end of the file rather than the beginning PrintWriter outPrintWriter = new PrintWriter(new BufferedWriter( new FileWriter(file, true))); outPrintWriter.print("Version: " + 1.01); outPrintWriter.println(); outPrintWriter.write("File name: test.txt"); outPrintWriter.println("此处换行"); outPrintWriter.append("We'll see!"); outPrintWriter.close(); // outPrintWriter.flush()也可 } }
上述程序运行结果,在D盘的test.txt文件写入如下内容
系统预设文本文件的读入操作是缓冲式输入,其常用模式为:
BufferedReader
-->FileReader
import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; public class InputReader { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("D:"+File.separator+"test.txt"); BufferedReader in = new BufferedReader(new FileReader(file)); /* 利用readLine()读取文件内容 * String line = in.readLine(); * while(line != null){ * System.out.println(line); * line = in.readLine(); * } * in.close(); * */ String line = ""; int ch = in.read(); //读入一个字符 while (ch != -1) { //read()读到文件结束时,其读入内容为-1 line += (char)ch; if (ch == '\n') {//遇到换行符,输出 System.out.print(line); line = ""; } else if(ch == '1') {//遇到'1',跳过3个字符 in.skip(3); } ch = in.read(); } System.out.print(line); in.close(); } }
运行结果:
Version: 1
File name: test.txt此处换行
We'll see!
进行二进制文件输出操作的一般模式为:
DataOutputStream -->BufferedOutputStream (可选项,但推荐使用) -->FileOutputStream
import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class OutputWriter2 { public static void main(String[] args) throws IOException { File file = new File("D:" + File.separator + "test2.txt"); DataOutputStream out = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(file))); out.writeBoolean(true); //1字节 out.writeChar(65); //2字节 out.writeChar('A'); //2字节 out.writeChars("Java"); //8字节 out.writeByte(99); //1字节 out.writeByte('B'); //1字节 out.writeBytes("Coding"); //6字节 out.writeUTF("Java"); //6字节 out.close(); System.out.println("File size: "+out.size()+" bytes"); } }
关于上述程序中的writeBytes(String s)和writeUTF(String s),writeBytes将字符串中每个字符的高八位丢弃将低八位写入字节,所以writeBytes("Coding")写入6个字节,writeByte也类似,write('B')也丢掉了字符'B'的高字节;writeUTF是将字符串以UTF-8的格式写的,它首先用两个字节表示字符串的字节数,然后将字符按UTF-8格式写入(对于ASCII码能表示的字符,UTF-8也使用一般字节来表示),所以writeUTF("Java")写入2+4个字节。
二进制文件输入操作一般模式:
DataInputStream -->BufferedInputStream (可选项,推荐使用) -->FileInputStream
import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class InputReader2 { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("D:" + File.separator + "test2.txt"); DataInputStream in = new DataInputStream(new BufferedInputStream( new FileInputStream(file))); boolean flag = in.readBoolean(); char ch1 = in.readChar(); char ch2 = in.readChar(); String name1 = ""; for (int i = 0; i < 4; i++) { name1 += in.readChar(); } byte b1 = in.readByte(); byte b2 = in.readByte(); byte[] bs = new byte[6]; for (int i = 0; i < 6; i++) { bs[i] = in.readByte(); } String name2 = in.readUTF(); in.close(); System.out.println(flag); System.out.println(ch1); System.out.println(ch2); System.out.println(name1); System.out.println(b1); System.out.println(b2); System.out.println(bs); System.out.println(name2); } }
注意:读操作时,要按写入的顺序读。上述程序的运行结果:
true A A Java 99 66 [B@c17164 Java
这一篇先写到这里,下一篇写对象序列化IO和RandomAccessFile