前面把最基本的输入输出流的概念性东西说过了,今天主要说明一下文件输入输出在文件中最基本的应用,先看文件的创建和基本的方法应用
import java.io.File; /** * 文件的基本操作 * * @author 王伟 * */ public class FileDemo { public static void main(String args[]) { // 使用字符串表示文件路径 String path = "F:\\aba\\Student.txt"; // 使用File对象表示文件 File file = new File(path); //创建文件夹 //boolean b= file.mkdirs(); //System.out.println("是否创建:"+b); //获得文件的路径 String path1 = file.getPath(); System.out.println("路径:"+path1); //得到绝对路径 String path2 = file.getAbsolutePath(); System.out.println("绝对路径:"+path2); //创建多级文件夹 file.mkdirs(); try { // 创建标准文件 file.createNewFile(); } catch (Exception ef) { ef.printStackTrace(); } // 判断文件是否存在 boolean b= file.exists(); System.out.println("是否存在:" + b); // 是否可读 boolean b1 = file.canRead(); System.out.println("是否可读" + b1); // 是否可写 boolean b2 = file.canWrite(); System.out.println("是否可写" + b2); //文件大小 long len = file.length(); System.out.println("文件大小:"+len); } }
这段代码主要有几个基本常用的方法没什么好说的演示一下就过了,方法的作用注释也写得非常的清楚,接下来再看下一段代码,统计文件夹下面所有文件的个数,这个过程主要分为三步,第一步判断文件是不是存在,第二部判断这是不是一个文件夹,两步之后确定是一个文件夹以后,用数组方法遍历出文件夹下面文件的个数
import java.io.File; /** * 文件工具类 * * @author 王伟 * */ public class FileUtil { public static void main(String args[]){ String path = "F:\\eclipse"; int num = countFile(path); System.out.println("统计完毕,共有"+num+"个文件"); } /** * 统计指定目录下的标准文件个数 * * @param path * 要统计的文件路径 * @return 返回统计的标准文件个数 */ public static int countFile(String path) { int result = 0;//文件个数 // 根据路径创建文件对象 File file = new File(path); if(!file.exists()){ System.out.println("文件路径不存在!"); return 0; } //得到该文件中的所有子文件 File[] fs = file.listFiles(); //如果fs为null,表示file不是一个文件夹 if(fs==null){ System.out.println("给定的路径不是一个文件夹!"); return 0; } //如果能够执行到这里,证明肯定是一个文件夹了 //遍历数组,统计文件个数: for(int i=0;i<fs.length;i++){ File f = fs[i]; //获得文件路径 String str = f.getAbsolutePath(); //如果是一个标准文件 if(f.isFile()){ result++; System.out.println("找到一个文件:"+str); }else if(f.isDirectory()){ //如果是一个文件夹,就递归调用,统计这个子文件夹中的文件 result+=countFile(str); }else{ } } return result; } }
代码注释清晰,读者可以根据代码加深理解,接下来看一下文件复制基本操作,先看代码
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; /** * 文件流的操作 * * @author 王伟 * */ public class FIleIO { public static void main(String args[]) { String path = "D:\\nnn\\src"; String path1 = "F:\\aba"; // 创建文件对象 File f = new File(path); File f1 = new File(path1); String str = readFile(f); writeFile(f1, str); } public static void writeFile(File f, String str) { try { // 根据文件对象建立文件输出流 // 参数1:要写入数据的文件路径 // 参数2:是否将数据追加到文件末尾 FileOutputStream fos = new FileOutputStream(f, false); // 得到要输出的字符串的字节数组 byte[] bs = str.getBytes(); for (byte b : bs) { // 写出字节 fos.write(b); } // 强制将管道中的数据输出到目标,保证数据写完整 fos.flush(); // 关闭输出流 fos.close(); } catch (Exception ef) { ef.printStackTrace(); } } public static void writeFile2(File f, String str) { try { // 根据文件对象建立文件输出流 // 参数1:要写入数据的文件路径 // 参数2:是否将数据追加到文件末尾 FileOutputStream fos = new FileOutputStream(f, false); // 得到要输出的字符串的字节数组 byte[] bs = str.getBytes(); // 将数组写入输出流 fos.write(bs); // 强制将管道中的数据输出到目标,保证数据写完整 fos.flush(); // 关闭输出流 fos.close(); } catch (Exception ef) { ef.printStackTrace(); } } /** * 读取文件数据的方法 * * @param f * 要读读取的文件路径 */ public static String readFile(File f) { try { // 根据文件建立建立文件输入流 FileInputStream fis = new FileInputStream(f); int len = fis.available();// 得到流中的字节数 //方式一:通过流的长度决定读的次数 // while(len>0){ // //读取流中的一个字节 // int b = fis.read(); // System.out.println("字节:"+b); // len = fis.available(); // } byte[] bs = new byte[len]; //方式二,通过读取的字节判断读取的次数〉若为-1,就结束 // int i=0; // int b = fis.read(); // while(b!=-1){ // System.out.println("字节:"+b); // //将字节保存到字节数组 // bs[i]=(byte)b; // i++; // // b = fis.read(); // } //方式三:直接将数据读取到字节数组,数组有多大,就读取多少次 fis.read(bs); fis.close(); //将字节数组转成字符串 String str = new String(bs); System.out.println(str); return str; } catch (Exception ef) { ef.printStackTrace(); } return null; } }
以上读取文件的方法有三种各有各的有点,读者可以根据自己的喜好或者考虑到效率问题自己选择更适合的方法,这里不强调具体哪种方法更好,这样的复制文件要是考虑到效率问题,太浪费时间,那有没有更好的方法来解决此问题呢?在javaIO中还提供了带缓冲的输入输出流,这就相当于打包输入输出一般,一个包一个包的输出,举个例子,你们一个班都要去岳阳,坐车车上可以容纳40人,你总要一趟车只坐一个人然后一趟一趟的跑,这样效率太低,要是一次四十人效率高节约资源,所以java中提供这样的好方法接下来将上面的代码稍作改动就有了更高效率的复制
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; /** * 文件流的操作 * * @author 王伟 * */ public class FIleIO { public static void main(String args[]) { String path = "F:\\XiGua Yingshi\\dianying"; String path1 = "F:\\aba\\dianying"; // 创建文件对象 File f = new File(path); File f1 = new File(path1); long t1 = System.currentTimeMillis(); byte[] str = readFile(f); writeFile(f1, str); long t2 = System.currentTimeMillis(); System.out.println("复制完成!耗时:" + (t2 - t1) + "ms"); } public static void writeFile(File f, byte[] bs) { try { // 根据文件对象建立文件输出流 // 参数1:要写入数据的文件路径 // 参数2:是否将数据追加到文件末尾 FileOutputStream fos = new FileOutputStream(f, false); // 将输出流包装成缓冲输出流 BufferedOutputStream bos = new BufferedOutputStream(fos); // 使用缓冲流将数组写入输出流 bos.write(bs); // 强制将管道中的数据输出到目标,保证数据写完整 bos.flush(); // 关闭输出流 fos.close(); } catch (Exception ef) { ef.printStackTrace(); } } /** * 读取文件数据的方法 * * @param f * 要读读取的文件路径 */ public static byte[] readFile(File f) { try { // 根据文件建立建立文件输入流 FileInputStream fis = new FileInputStream(f); // 将文件输入流打包成缓冲输入流 BufferedInputStream bis = new BufferedInputStream(fis); int len = bis.available();// 得到流中的字节数 // 方式一:通过流的长度决定读的次数 // while(len>0){ // //读取流中的一个字节 // int b = fis.read(); // System.out.println("字节:"+b); // len = fis.available(); // } byte[] bs = new byte[len]; // 方式二,通过读取的字节判断读取的次数〉若为-1,就结束 // int i=0; // int b = fis.read(); // while(b!=-1){ // System.out.println("字节:"+b); // //将字节保存到字节数组 // bs[i]=(byte)b; // i++; // // b = fis.read(); // } // 方式三:直接将数据读取到字节数组,数组有多大,就读取多少次 bis.read(bs); fis.close(); return bs; } catch (Exception ef) { ef.printStackTrace(); } return null; } }
这里这两种方法BufferedInputStream 和BufferedOutputStream 可以把文件复制的效率提高几百倍甚至几千倍,这就是带缓冲的输入输出流的好处,接下来再看学序列化的基本应用,看以下部分代码,在序列化和没有序列化的对比,先看没序列化的这段代码
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; /** * 学生操作类 * * @author 王伟 * */ public class StudentDB { public static void main(String args[]) { File file = new File("F:\\abc\\student.db"); // 字节数4+1+4+4+6 Student stu = new Student("王麻子", (byte) 20, 95.5F, 20120101, "年轻力壮"); saveStudent(file, stu); System.out.println("保存成功!"); //从文件中读取对象 Student stu2 = getStudent(file); System.out.println(stu2.name); System.out.println(stu2.age); System.out.println(stu2.score); System.out.println(stu2.num); System.out.println(stu2.desc); } /** * 将学生对象保存到指定的文件 * * @param file * 保存学生的文件 * @param stu * 要保存的学生对象 */ public static void saveStudent(File file, Student stu) { try { // 建立文件输出流 FileOutputStream fos = new FileOutputStream(file); // 将输出流包装成基本数据类型输出流 DataOutputStream dos = new DataOutputStream(fos); // 操作字符串的时候,还是采用字节来操作 // //获得名字的字节数组 byte[] names = stu.name.getBytes(); // 写名字字符串的字节长度 dos.writeInt(names.length); // 写名字 dos.write(names); // 写年龄 dos.writeByte(stu.age); // 写分数 dos.writeFloat(stu.score); // 学号 dos.writeInt(stu.num); // 简介 byte[] descs = stu.desc.getBytes(); dos.writeInt(descs.length); dos.write(descs); // 强制输出 dos.flush(); // 关闭流 fos.close(); } catch (Exception ef) { ef.printStackTrace(); } } /** * 从文件中获取学生对象 * * @param file * 要获取对象的文件 * @return 返回获得的学生对象 */ public static Student getStudent(File file) { try { // 建立文件输入流 FileInputStream fis = new FileInputStream(file); // 包装成基本数据类型流 DataInputStream dis = new DataInputStream(fis); // 读取名字的长度 int len = dis.readInt(); // 定义字节数组 byte[] names = new byte[len]; // 从流中读取字节填满数组 dis.read(names); // 将字节数组转成字符串 String name = new String(names); // 读取年龄 byte age = dis.readByte(); float score = dis.readFloat(); int num = dis.readInt(); // 简介 int len2 = dis.readInt(); byte[] descs = new byte[len2]; dis.read(descs); String desc = new String(descs); // 根据属性的值创建学生对象 Student stu = new Student(name, age, score, num, desc); return stu; } catch (Exception ef) { ef.printStackTrace(); } return null; } }
再看序列化以后的这段代码(部分代码)
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * 学生操作类 * * @author 王伟 * */ public class StudentDB2 { public static void main(String args[]) { File file = new File("F:\\abc\\student.db"); // 字节数4+1+4+4+6 Student stu = new Student("王麻子", (byte) 20, 95.5F, 2011, "年轻力壮"); saveStudent(file, stu); System.out.println("保存成功!"); // 从文件中读取对象 Student stu2 = getStudent(file); System.out.println(stu2.name); System.out.println(stu2.age); System.out.println(stu2.score); System.out.println(stu2.num); System.out.println(stu2.desc); } /** * 将学生对象保存到指定的文件 * * @param file * 保存学生的文件 * @param stu * 要保存的学生对象 */ public static void saveStudent(File file, Student stu) { try { // 建立文件输出流 FileOutputStream fos = new FileOutputStream(file); // 将输出流包装成对象输出流 ObjectOutputStream dos = new ObjectOutputStream(fos); //将对象写到输出流 dos.writeObject(stu); // 强制输出 dos.flush(); // 关闭流 fos.close(); } catch (Exception ef) { ef.printStackTrace(); } } /** * 从文件中获取学生对象 * * @param file * 要获取对象的文件 * @return 返回获得的学生对象 */ public static Student getStudent(File file) { try { // 建立文件输入流 FileInputStream fis = new FileInputStream(file); // 包装成对象输入流 ObjectInputStream dis = new ObjectInputStream(fis); //读取对象 Student stu = (Student) dis.readObject(); return stu; } catch (Exception ef) { ef.printStackTrace(); } return null; } }
从上面代码可以清晰的看出,序列化省事的多,节约时间又简单,区别就在于序列化是以对象为输入输出的,而上面是以数据流作为输入输出的,所以序列化更简单,今天基本文件操作就说到这,下面将重点说明有点难度的文件类型