文件管理和输入输出流

文件管理与 I/O 流

文件管理

Java 语言使用 File 类对文件和目录进行操作,查找文件时需要实现 FilenameFilter 或 FileFilter 接口。另外,读写文件内容可以通过 FileInputStream、FileOutputStream、FileReader 和 FileWriter 类实现,它们属于 I/O 流。这些类和接口全部来 源于 java.io 包。

File类

File 类表示一个与平台无关的文件或目录。File 类名很有欺骗性,初学者会误认为是 File 对象只是一个文件,但它也可能是一个目录

File 类中常用的方法如下。

  1. 构造方法

    • File(String path):如果path是实际存在的路径,则该File对象表示的是目录;如果path是文件名,则该File对象表示的是文件

    • File(String path,String name):path是路径名,name是文件名

    • File(File dir,String name):dir是路径对象,name是文件名

  2. 获得文件名

    • String getName():获得文件的名称,不包括路径

    • String getPath():获取文件的路径

    • String getAbsolutePath():获得文件的绝对路径

    • String getParent():获得文件的上一级目录名

  3. 文件属性测试

    • boolean exists();测试当前File对象所表示的文件是否存在

    • boolean canWrite():测试当前文件是否可读

    • boolean canRead(): 测试当前文件是否可读

    • boolean isDirectory():测试当前文件是否是目录

  4. 文件操作

    • long lastModified(): 获得文件最近一次修改的时间

    • long length():获取文件的长度,一字节为单位

    • boolean delete():删除当前文件。成功返回true,否则返回false。

    • boolean renameTo(File dest): 将重新命名当前File对象所表示的文件。陈宫返回true,否则返回false。

  5. 目录操作

    • boolean mkdir():创建当前File对象指定的目录

    • String[] list():返回当前目录下的文件和目录,返回值是字符串数组。

    • String[] list(FilenameFilter filter):返回当前目录下满足指定过滤器的文件和目录,参数是实现== == FilenameFilter接口对象,返回值是字符串数组。

    • File[] listFiles():返回当前目录下的文件和目录,返回值是 File 数组。

    • File[] listFiles(FilenameFilter filter):返回当前目录下满足指定过滤器的文件和目录, 参数是实现 FilenameFilter 接口对象,返回值是 File 数组。

    • File[] listFiles(FileFilter filter):返回当前目录下满足指定过滤器的文件和目录,参 数是实现 FileFilter 接口对象,返回值是 File 数组。

       

      对目录操作有两个过滤器接口:FilenameFilter 和 FileFilter。它们都只有一个抽象方法 accept, FilenameFilter 接口中的 accept 方法如下:

      • boolean accept(File dir, String name):测试指定 dir 目录中是否包含文件名为 name 的文件。

      • FileFilter 接口中的 accept 方法如下: boolean accept(File pathname):测试指定路径名是否应该包含在某个路径名列表 中。

 

 


注意:路径中会用到路径分隔符,路径分隔符在不用平台上是有区别的,,UNIX、Linux和 macOS中使用正斜杠“/”,而Windows下使用反斜杠“\”。Java是支持两种写法, 但是反斜杠“\”属于特殊字符,前面需要加转义符。例如C:\Users\a.java在程序代码 中应该使用C:\ \Users\a.java表示,或表示为C:/Users/a.java也可以。

案例:文件过滤

package com.qian.File;

import java.io.File;
import java.io.FilenameFilter;

public class FileFilter {
    /**
     * 从指定的目录中列出文件信息。
     * @param args
     */

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //用File对象表示一个目录,.表示当前目录
        File dir = new File("./TestDir");
        //创建html文件过滤器
        Filter filter = new Filter("html");
        
        System.out.println("html文件目录:"+dir);
        
        //列出目录TestDir下,文件后缀名为HTML的所有文件
        String files[] = dir.list(filter);
        
        //遍历文件列表
        for(String fileName:files) {
            //为目录TestDir下的文件或目录创建File对象
            File f = new File(dir,fileName);
            
            // 如果该f对象是文件,则打印文件名    
            if (f.isFile()) {
                System.out.println("文件名:" + f.getName());
                System.out.println("文件绝对路径:" + f.getAbsolutePath());
                System.out.println("文件路径:" + f.getPath());
                } else { 
                    System.out.println("子目录:" + f);
                    }   
        }
    }

}



//自定义基于文件扩展名的文件过滤器 
class Filter implements FilenameFilter {          

// 文件扩展名  
    String extent; 

// 构造方法  
    Filter(String extent) {
    this.extent = extent;
    } 
    public boolean accept(File dir, String name) {
    // 测试文件扩展名是否为extent所指定的   
        return name.endsWith("." + extent);
    }
 }

 



 

 

I/O流概述

概述: 以CPU为中心,从外部设备读取数据到内存,进而再读入到CPU,这是输入(Input, 缩写I)过程;将内存中的数据写入到外部设备,这是输出(Output,缩写O)过程。 所以输入输出简称为I/O。


字节输入流

字节输入流根类是 InputStream,如图 16-3 所示它有很多子类,这些类的说明如表 161 所示。

 

 

 

 

 

字节输出流

字节输出流根类是 OutputStream,如图 16-4 所示它有很多子类,这些类的说明如表 16-2 所示。

 

 

 

 

字符输入流

字符输入流根类是 Reader,这类流以 16 位的 Unicode 编码表示的字符为基本处理单 位。如图 16-5 所示它有很多子类,这些类的说明如表 16-3 所示。

 

 

 

 

字符输出流

字符输出流根类是 Writer,这类流以 16 位的 Unicode 编码表示的字符为基本处理单 位。如图 16-6 所示它有很多子类,这些类的说明如表 16-4 所示。

 

 

 

 

 

字节流

掌 握字节流的 API 先要熟悉它的两个抽象类:InputStream 和 OutputStream,了解它们有哪 些主要的方法。

InputStream

InputStream 是字节输入流的根类,它定义了很多方法,影响着字节输入流的行为。 下面详细介绍一下

  • int read():读取一个字节,返回0到255范围内的int字节值。如果因为已经到达流末尾,而且没有可用的字节,则返回值-1.

  • int read(byte b[]):读取多个字节,数据放到字节数组b中,返回值为实际读取的字节的数量,如果已经到达流末尾,而且没有可用的字符,则返回-1。

  • int read(byte b[],int off,int len):最多读取len个字节,数据放到以下标off开始字节数组b中,将读取的第一个字节存储在元素b[off]中,下一个存储在b[off+1]中,以此类推。返回值为实际读取的字节数量,如果因为已经到达流末尾,而且没有可用的字节,则返回-1。

  • void close():流操作完毕后必须关闭。

 

上述所有方法都会抛出IOException,因此使用时注意处理异常

OutputStream

OutputStream 是字节输出流的根类,它定义了很多方法,影响着字节输出流的行为。

  • void write(int b):将 b 写入到输出流,b 是 int 类型占有 32 位,写入过程是写入 b 的 8 个低位,b 的 24 个高位将被忽略。

  • void write(byte b[ ]):将 b.length 个字节从指定字节数组 b 写入到输出流。

  • void write(byte b[ ], int off, int len):把字节数组 b 中从下标 off 开始,长度为 len 的字节写入到输出流。

  • void flush():刷空输出流,并输出所有被缓存的字节。由于某些流支持缓存功能, 该方法将把缓存中所有内容强制输出到流中。

  • void close( ):流操作完毕后必须关闭。

 

上述所有方法都声明抛出 IOException,因此使用时要注意处理异常。

 


 

案例:文件复制

package com.qian.IO;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {
    /**
     * 复制文件
     * @param args
     */

    public static void main(String[] args)throws IOException {
        // TODO Auto-generated method stub
        FileInputStream in = new FileInputStream("TestDir/build.txt");
        FileOutputStream out = new FileOutputStream("TestDir/subDir/build.txt");
        
        //准备一个缓冲区
        byte[] buff = new byte[10];
        int len =-1;
        while((len = in.read(buff))!=-1) {
            String copyStr = new String(buff);
            //打印复制的字符串
            System.out.println(copyStr);
            out.write(buff, 0, len);
            //再读取一次
//            len= in.read(buff);
        }
        
        out.close();
        in.close();
    }

}

 


 

结果:本应写入文本的为 AI-162.3764568,实际输入正确

控制台输出:AI-162.376 456862.376

 

分析结果:为什么控制台输出的答案和我们写出的数据不一样?

源文件中内容是AI-162.3764568,共有14个字符,由于这些字符都属于ASCII 字符,因此14个字符需要14字节描述,需要读写两次才能完成复制。 第一次读取了10个字节,第二次执行len = in.read(buff)事实上执行了两次,第一次执行是len=4,然后从第一个位置开始覆盖上一次读取的数据,第二次执行时len为-1。

 

代码out.write(buffer, 0, len)是向输出流写入数据,与读取数据对应,数据写入 也调用了两次,第一次 len 为 10,将缓冲区 buffer 所有元素全部写入输出流;第二次 len 为 4,将缓冲区 buffer 所有前 4 个元素写入输出流。注意这里不要使用 void write(byte b[ ]) 方法,因为它没法控制第二次写入的字节数。 Java 从小白到大牛 精简版 211 / 237

上面的案例由于使用的字节输入输出流,所以不仅可以复制文本文件,还有复制二 进制文件。

 

使用字节缓冲流

BufferedInputStream 和 BufferedOutputStream 称为字节缓冲流,使用字节缓冲流内置 了一个缓冲区,第一次调用 read 方法时尽可能多地从数据源读取数据到缓冲区,后续再 到用 read 方法时先看看缓冲区中是否有数据,如果有则读缓冲区中的数据,如果没有再 将数据源中的数据读入到缓冲区,这样可以减少直接读数据源的次数。通过输出流调用 write 方法写入数据时,也先将数据写入到缓冲区,缓冲区满了之后再写入数据目的地, 这样可以减少直接对数据目的地写入次数。使用了缓冲字节流可以减少 I/O 操作次数,提 高效率。 从图 16-3 和图 16-4 可见,BufferedInputStream 的父类是 FilterInputStream, BufferedOutputStream的父类是FilterOutputStream,FilterInputStream和FilterOutputStream 称为过滤流。过滤流的作用是扩展其他流,增强其功能。那么 BufferedInputStream 和 BufferedOutputStream 增强了缓冲能力。

 

BufferedInputStream 和 BufferedOutputStream 中主要方法都是继承自 InputStream 和 OutputStream 前面两个节已经详细介绍了,这里不再赘述。下面介绍一下它们的构造方 法,

BufferedInputStream 构造方法主要有:

  • BufferedInputStream(InputStream in):通过一个底层输入流in对象创建缓冲流对象,缓冲区大小是默认的,默认是8192.

  • BufferedInputStream(InputStream in , size):通过一个底层输入流in对选哪个创建流对象,size指定的缓冲区的大小,缓冲区大小应该是2的n次幂,这样可提供缓冲区的利用率

    BufferedOutputStream 构造方法主要有:

  • BufferedOutputStream(OutputStream out):通过一个底层输出流 out 对象创建缓 冲流对象,缓冲区大小是默认的,默认值 8192。

  • BufferedOutputStream(OutputStream out, int size):通过一个底层输出流 out 对象 创建缓冲流对象,size 指定的缓冲区大小,缓冲区大小应该是 2 的 n 次幂,这样 可提供缓冲区的利用率。

 

案例改造:文件复制

package com.qian.IO;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy2 {

/**
* 缓冲流实现文件复制
* @param args
*/
public static void main(String[] args)throws IOException {
// TODO Auto-generated method stub
FileInputStream fis = new FileInputStream("TestDir/jdk.zip");
FileOutputStream fos = new FileOutputStream("TestDir/subDir/jdk.zip");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);

//开始时间
long startTime = System.nanoTime();
//准备一个缓冲区
byte[] buff = new byte[2048];
//首先读取一次
int len =-1;
while((len = bis.read())!=-1){
//开始写入输入
bos.write(buff,0,len);
}

//结束时间
long elapsedTime = System.nanoTime()-startTime;
System.out.println("耗时:"+(elapsedTime/1000000.0)+"毫秒");
}

}


//耗时:34687.8186毫秒

 

 

字符流

Reader抽象类

Reader 是字符输入流的根类,它定义了很多方法,影响着字符输入流的行为。下面 详细介绍一下

  • int read():读取一个字符,返回值范围在0-65535之间,如果已经到达流末尾,则返回值-1.

  • int read(char[] cbuf):将字符读入到数组 cbuf 中,返回值为实际读取的字符的数量,如果因为已经到达流末尾,则返回值-1。

  • int read(char[] cbuf, int off, int len):最多读取 len 个字符,数据放到以下标 off 开 始字符数组 cbuf 中,将读取的第一个字符存储在元素 cbuf[off]中,下一个存储 在 cbuf[off+1]中,依次类推。返回值为实际读取的字符的数量,如果因为已经到 达流末尾,则返回值-1。

  • void close():流操作完毕后必须关闭。

上述所有方法都可以会抛出 IOException,因此使用时要注意处理异常。

Writer抽象类

Writer 是字符输出流的根类,它定义了很多方法,影响着字符输出流的行为。下面详 细介绍一下。

  • void write(int c):将整数值为 c 的字符写入到输出流,c 是 int 类型占有 32 位, 写入过程是写入 c 的 16 个低位,c 的 16 个高位将被忽略。

  • void write(char[] cbuf):将字符数组 cbuf 写入到输出流。

  • void write(char[] cbuf, int off, int len):把字符数组 cbuf 中从下标 off 开始,长度为 len 的字符写入到输出流。

  • void flush():刷空输出流,并输出所有被缓存的字符。由于某些流支持缓存功能, 该方法将把缓存中所有内容强制输出到流中。

  • void close( ):流操作完毕后必须关闭。

上述所有方法都可以会抛出 IOException,因此使用时要注意处理异常。


注意 Reader和Writer都实现了AutoCloseable接口,可以使用自动资源管理技术自动关闭 它们。


 

案例:文件复制

前面两个介绍了字符流常用的方法,下面通过一个案例熟悉一下它们的使用,该案 例实现了文件复制,数据源是文件,所以会用到文件输入流 FileReader,数据目的地也是 文件,所以会用到文件输出流 FileWriter。 FileReader 和 FileWriter 中主要方法都是继承自 Reader 和 Writer 前面两个节已经详细

介绍了,这里不再赘述。下面介绍一下它们的构造方法,FileReader 构造方法主要有:

  • FileReader(String fileName):创建 FileReader 对象,fileName 是文件名。如果文件 不存在则抛出 FileNotFoundException 异常。

  • FileReader(File file):通过 File 对象创建 FileReader 对象。如果文件不存在则抛出 FileNotFoundException 异常。

FileWriter 构造方法主要有:

  • FileWriter(String fileName):通过指定 fileName 文件名创建 FileWriter 对象。如果 fileName 文 件 存 在 , 但 如 果 是 一 个 目 录 或 文 件 无 法 打 开 则 抛 出 FileNotFoundException 异常。

  • FileWriter(String fileName, boolean append):通过指定 fileName 文件名创建 FileWriter 对象,append 参数如果为 true,则将字符写入文件末尾处,而不是写 入文件开始处。如果 fileName 文件存在,但如果是一个目录或文件无法打开则 抛出 FileNotFoundException 异常。

  • FileWriter(File file):通过 File 对象创建 FileWriter 对象。如果 file 文件存在,但如 果是一个目录或文件无法打开则抛出 FileNotFoundException 异常。

  • FileWriter(File file, boolean append):通过 File 对象创建 FileWriter 对象,append 参数如果为 true,则将字符写入文件末尾处,而不是写入文件开始处。如果 file 文件存在,但如果是一个目录或文件无法打开则抛出 FileNotFoundException 异 常。


注意:字符文件流只能复制文本文件,不能是二进制文件


package com.qian.IO;
​
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
​
public class FileCopy2 {
​
    /**
     * 缓冲流实现文件复制
     * @param args
     */
     public static void main(String[] args) { 
         
          try (FileReader in = new FileReader("./TestDir/build.txt");
            FileWriter out = new FileWriter("./TestDir/subDir/build.txt")) 
          { 
         
           // 准备一个缓冲区    
            char[] buffer = new char[10];    
              // 首先读取一次   
            int len = in.read(buffer); 
         
            while (len != -1) {
               String copyStr = new String(buffer); 
           
 
         
            // 打印复制的字符串     
               System.out.println(copyStr);
               // 开始写入数据    
               out.write(buffer, 0, len);
               // 再读取一次    
               len = in.read(buffer);
            } 
         
          } catch (FileNotFoundException e){ 
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();   
              }  
          } 
}
​
//控制台输出结果: AI-162.376 456862.37

 

6 

 

字符缓冲流

BufferedReader 和 BufferedWriter 称为字符缓冲流。BufferedReader 特有方法和构造 方法有:

  • String readLine():读取一个文本行,如果因为已经到达流末尾,则返回值 null。

  • BufferedReader(Reader in):构造方法,通过一个底层输入流 in 对象创建缓冲流 对象,缓冲区大小是默认的,默认值 8192。

  • BufferedReader(Reader in, int size):构造方法,通过一个底层输入流 in 对象创建 缓冲流对象,size 指定的缓冲区大小,缓冲区大小应该是 2 的 n 次幂,这样可提 供缓冲区的利用率。

 

BufferedWriter 特有方法和构造方法主要有:

  • void newLine():写入一个换行符。

  • BufferedWriter(Writer out):构造方法,通过一个底层输出流 out 对象创建缓冲 流对象,缓冲区大小是默认的,默认值 8192。

  • BufferedWriter(Writer out, int size):构造方法,通过一个底层输出流 out 对象创 建缓冲流对象,size 指定的缓冲区大小,缓冲区大小应该是 2 的 n 次幂,这样可 提供缓冲区的利用率。

 

案例:文件复制改造

package com.qian.IO;
​
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
​
public class FileCopy3 {
    /**
     * 复制文件
     * @param args
     */public static void main(String[] args) { 
         
          try (FileReader fis = new FileReader("./TestDir/JButton.html");    
                  BufferedReader bis = new BufferedReader(fis);     
                  FileWriter fos = new FileWriter("./TestDir/subDir/JButton.html");     
                  BufferedWriter bos = new BufferedWriter(fos)) { 
         
           // 首先读取一行文本    
                String line = bis.readLine();            
                while (line != null) {          
                  // 开始写入数据     
                  bos.write(line);          
                  //写一个换行符     
                  bos.newLine();          
                  // 再读取一行文本     
                  line = bis.readLine();    
            }    
                System.out.println("复制完成");   
              } catch (FileNotFoundException e) {    
                  e.printStackTrace();   
              } catch (IOException e) {    
                  e.printStackTrace();   
              }  
    } 
}
​
​

 


 

上述代码通过字节缓冲流 readLine 方法读取一行文本,当读取是文本为 null 时说明流已经读完了。代码写入文本到输出流,由于在输入流的 readLine 方法 会丢掉一个换行符或回车符,为了保持复制结果完全一样,因此需要在写完一个文本后, 调用输出流的 newLine 方法写入一个换行符。

字节流转为字符流

有时需要将字节流转换为字符流,InputStreamReader 和 OutputStreamWriter 是为实 现这种转换而设计的。

InputStreamReader 构造方法如下:

  • InputStreamReader(InputStream in):将字节流 in 转换为字符流对象,字符流使用 默认字符集。

  • InputStreamReader(InputStream in, String charsetName):将字节流 in 转换为字符 流对象,charsetName 指定字符流的字符集,字符集主要有:US-ASCII、ISO-88591 、 UTF-8 和 UTF-16 。 如 果 指 定 的 字 符 集 不 支 持 会 抛 出 UnsupportedEncodingException 异常。

OutputStreamWriter 构造方法如下:

  • OutputStreamWriter(OutputStream out):将字节流 out 转换为字符流对象,字符 流使用默认字符集。

  • OutputStreamWriter(OutputStream out,String charsetName):将字节流 out 转换为 字符流对象,charsetName 指定字符流的字符集,如果指定的字符集不支持会抛 出 UnsupportedEncodingException 异常。

就我而言,这两个方法用的很少

 

本章小结

本章主要介绍了 Java 文件管理和 I/O 流技术。读者需要熟悉 File 类使用。读者还需 要掌握字节流两个根类:InputStream 和 OutputStream,还有字符流的两个根类:Reader 和 Writer。了解一个常用的装饰器流,如:InputStreamReader、InputStreamReader、 BufferedReader、BufferedWriter、BufferedInputStream 和 BufferedOutputStream 等。

 

 

posted on 2020-07-20 16:22  虚空教官  阅读(224)  评论(0)    收藏  举报

导航