文件IO流API

文件IO流API

一、为什么使用IO流

​ 我们知道File类的对象可以表示计算机硬盘中的某个文件或文件夹,但是这些文件对象只是对实体文件的抽象描述,它只包含文件的一些属性和文件创建、修改、删除操作等方法,而不能对其存储的内容进行读写。在Java中我们引入了IO流的概念,用来进行文件读写操作。

在这里插入图片描述

这里以文件流为例,流就像是程序和目标文件之间的通道,构造流对象传入file对象后,则像是打开了它们两端之间通道,然后就可以利用这个通道来读写数据(也可以理解为传输数据),当流对象不再使用时,就将其关闭。

二、分类和继承关系

1. 分类
  • 按流的方向:输入流输出流
  • 按数据读写单位:字节流字符流
2. 继承关系
分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileReader

三、API简介和使用

1. InputStream

InputStream是以字节为单位来读取数据,它是一个抽象类,提供了读数据的几种方法,通过构造其实现子类FileInputStream的对象来进行读文件操作。

成员方法

方法签名描述
read()从流中读取一个字节并返回,如果读到了文件末尾则返回-1
read(byte[] buffer)从流中最多读取buf.length长的数据到buf中,返回实际读取的字节数,若读到文件末尾则返回-1
read(byte[] buffer,int offset,int len)从流中最多读取len-offset长的数据到buf中,从offset位置开始,返回实际读取的字节数,若读到文件末尾则返回-1
close()关闭字节输入流
FileInputStream

构造方法

方法签名描述
FileInputStream(String name)根据指定文件路径来构造流对象
FileInputStream(File file)根据指定file对象来构造流对象

当使用FileInputStream(String name)来构造对象时,该对象内部会帮我们自动的创建一个File类对象,FileInputStream要求File类对应的实体文件必须存在,否则会抛FileNotFoundException。

1. 读文件中的内容
  • 方式一:使用read()
//需要在当前工作路径下准备一个test.txt。并在文件中写入Hello,my friend.
public static void main(String[] args){
    //使用FileInputStream(String name)来构造流对象
    try(FileInputStream is = new FileInputStream("test.txt");){
        //读文件
        int b = 0;
        while((b = is.read()) != -1){
            //输出到控制台
            System.out.printf("%c ",b);
        }

    }catch(IOException e){
        e.printStackTrace();
    }
}

在这里插入图片描述

  • 方式二:使用read(byte[] buf) - 推荐使用
//需要在当前工作路径下准备一个test.txt。并在文件中写入Hello,my friend.
public static void main(String[] args){
    //使用FileInputStream(String name)来构造流对象
    try(FileInputStream is = new FileInputStream("test.txt");){
        //读文件
        byte[] buf = new byte[1024];
        int len = 0;
        while((len = is.read(buf)) != -1){
            //输出到控制台
            System.out.println(new String(buf,0,len));
        }

    }catch(IOException e){
        e.printStackTrace();
    }
}

在这里插入图片描述

2. 利用Scanner对象

我们通过字节流来读取字符并不方便,Java官方提供的java.util.Scanner类是用来将输入流中的数据分成tokens,用指定的分隔符来分割,并提供以不同类型来获取tokens值的方法。我们通常构造Scanner对象时会用new Scanner(System.in),其参数System.in代表标准输入流,通常情况下是键盘。

我们来看看Scanner对象的构造方法

在这里插入图片描述

Scanner类提供了很丰富的构造器,我们这里可以将自己构造的流对象传给Scanner构造器,从而初始化其内部的流对象,然后就可以使用Scanner对象的方法获取到流中的数据。

代码演示
//需要在当前工作路径下准备一个test.txt文件,并在文件中写入一些数据。
public static void main(String[] args) {
    //构造Scanner类对象,传入我们构造的输入流对象
    try(Scanner scanner = new Scanner(new FileInputStream("./test.txt"),"UTF-8")){
        while (scanner.hasNextLine()){
            //获取每一行内容,显示到控制台
            System.out.println(scanner.nextLine());
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

2. OutputStream

InputStream是以字节为单位来写数据,它是一个抽象类,提供了写数据的几种方法,通过构造其实现子类FileOutputStream的对象来进行写文件操作。

成员方法

方法签名描述
write(int b)将指定的字节输出到流中
write(byte[] buf)将指定buf数组中的所有内容输出到流中
write(byte[] buf, int offset, int len)将指定buf数组中的内容从offset开始最长到len输出到流中
flush()将流中缓冲区保存的数据全部输出
close()关闭字节输出流对象
FileOutputStream

构造方法

方法签名描述
FileOutputStream(File file)创建一个指向file对象的字节输出流对象
FileOutputStream(File file, boolean append)创建一个指向file对象的字节输出流对象,若append参数为true,则将数据输出到该文件的末尾而不是开头
FileOutputStream(String name)创建一个字节输出流对象,指向name命名的file对象
FileOutputStream(String name, boolean append)创建一个字节输出流对象,指向name命名的file对象,若append参数为true,则将数据输出到该文件的末尾而不是开头

FileOutputStream不要file对应的实体文件必须存在,当不存在时会自动创建。当存在时,若没有使用包含append参数的构造器或append参数为fasle,为对原文件中的内容进行清空然后再写入。

1. 写数据到文件中
  • 方式一:使用write(int b)
public static void main(String[] args) {
    //创建FileOutputStream对象,并指明文件路径
    try(FileOutputStream outputStream = new FileOutputStream("./demo.txt");){
        //写入数据
        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);
        outputStream.write(100);
    }catch (IOException e){
        e.printStackTrace();
    }
}

在这里插入图片描述

  • 方式二:使用write(byte[] buf)

    public static void main(String[] args) {
        //创建FileOutputStream对象,并指明文件路径
        try(FileOutputStream outputStream = new FileOutputStream("./demo.txt");){
            //写入数据
            String data = "Happy New Year!";
            outputStream.write(data.getBytes());
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    

在这里插入图片描述

  • 方式三:使用write(byte[] buf, int offset, int len)

    public static void main(String[] args) {
        //创建FileOutputStream对象,并指明文件路径
        try(FileOutputStream outputStream = new FileOutputStream("./demo.txt");){
            //写入数据
            String data = "Happy New Year!";
            outputStream.write(data.getBytes(),0,5);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    

在这里插入图片描述

2. 利用PrintWriter

使用字节流来写字符数据不是很方便,所以我们可以用Java提供的java.io.PrintWriter来进行数据写入,该类中包含了print、println、printf等方法,方便我们使用。但是使用该类需要将字节输入流先转换为字符输入流,对此Java也提供了具体的实现类OutputStreamWriter,来看看具体怎么使用。

/*
	使用PrintWriter,通过层层构造流的方式
	优点:可以显式调用各流的API,传入其不同流的构造器的其他参数
	缺点:代码稍长
*/
public static void main(String[] args) {
    //创建字节输出流对象,同时指明文件路径,并设置将数据追加到文件末尾
    try(FileOutputStream fos = new FileOutputStream("./demo.txt",true)){
        //创建转换流对象(字节输入流转字符输入流)
        try(OutputStreamWriter osw = new OutputStreamWriter(fos)){
            try(PrintWriter pw = new PrintWriter(osw)){
                //写数据
                pw.append("My friend.\n");
                pw.write("Wish you good luck in 2023!\n");
                pw.print("Your being makes this world better!\n");
            }
        }

    }catch (IOException e){
        e.printStackTrace();
    }
}
/*
    使用一次构造
    优点:代码简洁
    缺点:除了本层流以外,其他流不能指定初始化其他属性
*/
public static void main(String[] args) {
    try(PrintWriter pw = new PrintWriter("./demo2.txt")){
        pw.append("Happy New Year!\n");
        pw.write("My friend.\n");
        pw.print("Wish you good luck in 2023!\n");
        pw.println("Your being makes this world better!");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

四、小程序练习

1. 指定目录文件删除
/**
 * 编写一个小程序:
 *  扫描指定目录,查看目录中的各文件的文件名是否包含指定关键字(不包含目录),如果包含,就提示用户是否进行删除。
 *  目录中可能包含很多文件或子目录
 */
public class DirectorySearchDemo {
    //创建Scanner对象
    private static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        while(true){
            //输入指定目录
            System.out.println("请输入指定目录路径:");
            String basePath = scanner.next();
            File baseDic = new File(basePath);
            //检验所输入目录
            if(!baseDic.isDirectory()){
                System.out.println("当前输入的路径不是一个目录!");
                continue;
            }
            //输入关键字
            System.out.println("请输入要删除的文件的关键字:");
            String nameToDelete = scanner.next();
            //递归遍历目录,同时将包含关键字的文件对象存入list
            List<File> resultList = new ArrayList<>();
            search(baseDic,nameToDelete,resultList);
            //遍历resultList,询问用户是否删除文件
            System.out.println("共匹配到" + resultList.size() + "个包含" + nameToDelete + "的文件。");
            for(File file : resultList){
                System.out.println("请问是否确认删除文件\"" + file.getName() + "\"?");
                System.out.print("Y/N:");
                if("Y".equalsIgnoreCase(scanner.next())){
                    if(file.delete()){
                        System.out.println("已删除!");
                    }else{
                        System.out.println("删除取消。");
                    }
                }
            }
        }
    }

    //对给定根目录进行搜索,如有匹配指定关键字的文件就存入list里
    private static void search(File baseDic, String nameToDelete, List<File> resultList) {
        //查看当前根路径
        System.out.println("当前扫描路径:" + baseDic.getAbsoluteFile());
        //列出根目录下的所有文件
        File[] files = baseDic.listFiles();
        if(files == null || files.length == 0){
            //如果根目录为空目录,则当前递归终止
            return;
        }else{
            //遍历根目录下的所有文件
            for(File file : files){
                //如果当前文件是目录,则进行递归
                if(file.isDirectory()){
                    search(file,nameToDelete,resultList);
                }else{
                    //判断当前文件是否包含指定关键字
                    if(file.getName().contains(nameToDelete)){
                        resultList.add(file);
                    }
                }
            }
        }
    }
}
2. 指定目录文件查找
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * 编写一个小程序
 *  扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
 *  注意:我们现在的方案性能较差,所以尽量不要在太复杂的目录下或者大文件下实验
 */
public class DirectorySearchDemo2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true){
            //输入指定目录
            System.out.println("请输入指定目录:");
            String basePath = scanner.next();
            //检查目录输入正确性
            File rootFile = new File(basePath);
            if(!rootFile.isDirectory()){
                System.out.println("目录路径输入错误!");
                continue;
            }
            //输入关键字
            System.out.println("请输入关键字:");
            String key = scanner.next();
            //检查关键字合法性
            if("".equalsIgnoreCase(key) || key.length() == 0){
                System.out.println("关键字输入有误!");
                continue;
            }
            //递归遍历目录,将匹配的文件保存到list里
            List<File> result = new ArrayList<>();
            dirSearch(rootFile,key,result);
            //遍历result,输出匹配的文件名
            System.out.println("共匹配到" + result.size() + "个包含" + key + "的文件。");
            for(File file : result){
                System.out.println(file.getName());
            }
        }
    }

    //遍历根目录,将符合条件的文件存到list里
    private static void dirSearch(File rootFile, String key, List<File> result) {
        //查看当前根路径
        System.out.println("当前扫描路径:" + rootFile.getAbsoluteFile());
        //列出当前目录下的所有文件
        File[] files = rootFile.listFiles();
        if(files == null || files.length == 0){
            //根目录为空就终止递归
            return;
        }else{
            //遍历根目录中的子文件
            for(File file : files){
                if(file.isDirectory()){
                    //当前file是子目录
                    dirSearch(file, key, result);
                }else{
                    //当前file是普通文件,检查是否包含关键字
                    if(isMatchKey(file,key)){
                        result.add(file);
                    }
                }
            }
        }
    }
    
    //检查指定文件的文件名或内容中是否包含指定关键字
    private static boolean isMatchKey(File file, String key) {
        //检查文件名
        if(file.getName().contains(key)){
            return true;
        }else{
            //检查文件内容
            try(FileInputStream inputStream = new FileInputStream(file);){
                //使用字符串查找子串的方式
                StringBuilder str = new StringBuilder();
                //通过Scanner对象扫描文件内容
                Scanner scanner = new Scanner(inputStream, "UTF-8");
                while (scanner.hasNextLine()){
                    //拼接到str中,使之成为一个长字符串
                    str.append(scanner.nextLine());
                }
                //检查该长字符串中是否包含key子串
                return str.indexOf(key) != -1;
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        return false;
    }
}

3. 文件复制
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

/**
 * 实现对普通文件的复制
 */
public class FileCopyDemo {
    public static void main(String[] args) {
        //输入源文件和目标文件路径
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入源文件路径:");
        String sourcePath = scanner.next();
        System.out.println("请输入目标文件路径:");
        String destPath = scanner.next();

        //对两个路径进行校验
        //是否相同
        if(sourcePath.equalsIgnoreCase(destPath)){
            System.out.println("源文件与目标文件路径不能相同!");
            return;
        }
        //源文件是否为普通文件
        File srcFile = new File(sourcePath);
        if(!srcFile.isFile()){
            System.out.println("源文件路径输入错误!");
            return;
        }
        //目标文件是否存在
        File destFile = new File(destPath);
        if (destFile.isFile()){
            System.out.println("目标文件已存在!");
            return;
        }

        //进行文件拷贝,创建源文件输入流和目标文件输出流
        try (FileInputStream inputStream = new FileInputStream(srcFile);
             FileOutputStream outputStream = new FileOutputStream(destFile)) {
            //创建缓冲区
            byte[] buf = new byte[1024];
            while (true){
                //读取源文件输入流数据
                int len = inputStream.read(buf);
                if(len == -1){
                    break;
                }
                //输出到目标文件
                outputStream.write(buf,0,len);
            }
            System.out.println("复制成功!");
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

文章为本人独立编写,难免会有错误之处。
如发现有误,恳请评论提出!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值