文件内容的读写 (IO) —— 数据流

导读:本篇文章讲解 文件内容的读写 (IO) —— 数据流,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、什么是数据流

在这里插入图片描述

流:stream
把读写文件操作比喻成”水流”,水的特点:流动起来绵延不断~~
在这里插入图片描述
在这里插入图片描述

输入还是输出,是以CPU/内存为中心来判断!!!

二、字节流输入InputStream

2.1 InputStream 概述

方法

返回值类型 方法签名 说明
int read() 读取一个字节的数据,返回 -1 代表已经完全读完了
int read(byte[] b) 最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表以及读完了
int read(byte[] b, int off, int len) 最多读取 len – off 字节的数据到 b 中,从 off 开始放入,返回实际读到的数量;-1 代表以及读完了
void close() 关闭字节流

说明
InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用 FileInputStream

2.2 FileInputStream 的使用

构造方法

签名 说明
FileInputStream(File file) 利用 File 构造文件输入流
FileInputStream(String name) 利用文件路径构造文件输入流

在这里插入图片描述
代码示例

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class Demo {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("./bbb.txt");  // 内容为"hello world"
        while (true) {
            int b = inputStream.read();
            if (b == -1) {
                break;
            }
            System.out.println(b);
        }
        inputStream.close();
    }
}

运行结果:
在这里插入图片描述

三、字节流输出OutputStream

3.1 OutputStream 概述

方法

返回值类型 方法签名 说明
void write(int b) 写入要给字节的数据
void write(byte[] b) 将 b 这个字符数组中的数据全部写入 os 中
int write(byte[] b, int off, int len) 将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
void close() 关闭字节流
void flush() 重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。

说明
OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,所以使用 FileOutputStream。

3.2 FileOutputStream 的使用

构造方法

签名 说明
FileOutputStream(File file) 利用 File 构造文件输出流
FileOutputStream(String name) 利用文件路径构造文件输出流

在这里插入图片描述
代码示例

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Demo {
    // 进行写文件
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("./bbb.txt");

        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);

        outputStream.close();
    }
}

四、字符流输入 Reader

4.1 Reader 与 FileReader

同样,Reader是一个抽象类,需要实现类FileReader!

方法
在这里插入图片描述
代码示例

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 使用字符流读文件
        Reader reader = new FileReader("./bbb.txt");
        while (true) {
            int ret = reader.read();
            if (ret == -1) {
                break;
            }
            char ch = (char)ret;
            System.out.println(ch);
        }
        reader.close();
    }
}

4.2 利用 Scanner 进行字符读取

上述例子中,我们看到了对字符类型直接使用 InputStream 进行读取是非常麻烦且困难的,所以,我们使用一种我们之前比较熟悉的类来完成该工作,就是 Scanner 类。

构造方法

签名 说明
Scanner(InputStream is, String charset) 使用 charset 字符集进行 is 的扫描读取

代码示例

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 使用 Scanner 读文本文件
        InputStream inputStream = new FileInputStream("./bbb.txt");

        Scanner scanner = new Scanner(inputStream);
        while (scanner.hasNext()) {
            System.out.println(scanner.next());
        }

        inputStream.close();
    }
}

五、字符流输出 Writer

5.1 Writer 与 FileWriter

同样,Writer是一个抽象类,需要实现类FileWriter!

方法
在这里插入图片描述

代码示例

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 使用字符流来写文件.
        Writer writer = new FileWriter("./bbb.txt");
        writer.write("hello world");
        writer.close();
    }
}

5.2 利用 PrintWriter 进行字符写入

代码示例

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 针对写文本文件来说, 还可以使用 PrintWriter 来简化开发.
        try (OutputStream outputStream = new FileOutputStream("./bbb.txt")) {
            PrintWriter writer = new PrintWriter(outputStream);

            writer.println();
            writer.printf("a = %d\n", 10);
        }
    }
}

若没有写进文件中,可能是因为没有刷新缓冲区,此时可以调用 flush() 方法!!!

在这里插入图片描述

在这里插入图片描述

六、内存泄漏问题

在这里插入图片描述

客户端一般不害怕这种资源泄露问题 (客户端程序用一会儿就关了);服务器是害怕这种问题的 (服务器需要长期运行)!!!

一些代码中,如果 close() 前面代码抛出异常了,此时 close() 就执行不到了!因此我们通常要使用 finally 或 try with resources 结构来避免内存泄漏问题!!!:
把要关闭的对象写到 try() 里,当 try 结束,就会自动调用到对应对象的 close() 方法!
并且支持一个 () 放多个对象,多个对象的创建之间使用 ; 分隔就行了~~

七、例题

例一: 扫描指定目录,并找到名称中包含指定字符的所有普通文件 (不包含目录),并且后续询问用户是否要删除该文件

在这里插入图片描述
代码实现:

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 1. 让用户输入了必要的信息
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的路径: ");
        File rootDir = new File(scanner.next());
        if (!rootDir.isDirectory()) {
            System.out.println("您输入的目录不存在!");
            return;
        }
        System.out.println("请输入要搜索的关键词: ");
        String toDelete = scanner.next();

        // 2. 遍历目录, 需要借助一个核心方法, listFiles()
        scanDir(rootDir, toDelete);
    }

    // 借助这个方法进行递归遍历
    private static void scanDir(File rootDir, String toDelete) throws IOException {
        System.out.println("当前访问: " + rootDir.getCanonicalPath());
        File[] files = rootDir.listFiles();
        if (files == null) {
            // 说明 rootDir 是一个空的目录~~
            return;
        }
        // 如果目录非空, 则循环遍历每个元素.
        for (File f : files) {
            if (f.isDirectory()) {
                scanDir(f, toDelete);
            } else {
                // 不是目录, 普通文件, 判定文件名是否符合要求, 是否要进行删除
                checkDelete(f, toDelete);
            }
        }
    }

    private static void checkDelete(File f, String toDelete) throws IOException {
        if (f.getName().contains(toDelete)) {
            System.out.println("该单词" + toDelete + " 被 " + f.getCanonicalPath() + " 包含了, 是否要删除? (Y/N)");
            Scanner scanner = new Scanner(System.in);
            String choice = scanner.next();
            if (choice.equals("Y") || choice.equals("y")) {
                f.delete();
            }
        }
    }
}

例二: 进行普通文件的复制

在这里插入图片描述
代码实现:

import java.io.*;
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
        // 实现文件复制.
        // 1. 先输入要复制哪个文件(源文件), 以及把这个文件复制到哪里去(目标文件)~~
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入源文件: ");
        // srcFile 形如 d:/cat.jpg
        File srcFile = new File(scanner.next());
        System.out.println("请输入目标文件: ");
        // destFile 形如 d:/cat2.jpg
        File destFile = new File(scanner.next());
        if (!srcFile.isFile()) {
            System.out.println("输入的源文件有误!");
            return;
        }
        if (!destFile.getParentFile().isDirectory()) {
            System.out.println("输入的目标文件有误!");
            return;
        }
        if (destFile.isDirectory()) {
            System.out.println("输入的目标文件有误!");
            return;
        }
        // 2. 打开源文件, 按照字节读取内容, 依次写入到目标文件中.
        try (InputStream inputStream = new FileInputStream(srcFile);
             OutputStream outputStream = new FileOutputStream(destFile)) {
            // 读 src 的每个字节, 写入到 dest 中.
            while (true) {
                int ret = inputStream.read();
                if (ret == -1) {
                    break;
                }
                outputStream.write(ret);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

例三: 扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)

在这里插入图片描述
代码实现:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 1. 输入路径和要查询的关键词
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的路径: ");
        File rootDir = new File(scanner.next());
        System.out.println("请输入要查询的词: ");
        String toFind = scanner.next();

        // 2. 递归的扫描目录.
        scanDir(rootDir, toFind);
    }

    private static void scanDir(File rootDir, String toFind) throws IOException {
        File[] files = rootDir.listFiles();
        if (files == null) {
            return;
        }
        for (File f : files) {
            if (f.isDirectory()) {
                scanDir(f, toFind);
            } else {
                checkFile(f, toFind);
            }
        }
    }

    private static void checkFile(File f, String toFind) throws IOException {
        // 1. 先检查文件名
        if (f.getName().contains(toFind)) {
            System.out.println(f.getCanonicalPath() + " 文件名中包含 " + toFind);
        }
        // 2. 再检查文件内容
        try (InputStream inputStream = new FileInputStream(f)) {
            StringBuilder stringBuilder = new StringBuilder();
            Scanner scanner = new Scanner(inputStream);
            // 这个地方需要按行读取了~~
            while (scanner.hasNextLine()) {
                stringBuilder.append(scanner.nextLine() + "\n");
            }
            if (stringBuilder.indexOf(toFind) > -1) {
                System.out.println(f.getCanonicalPath() + " 文件内容包含 " + toFind);
            }
        }
    }
}

注意:我们现在的方案性能较差,所以尽量不要在太复杂的目录下或者大文件下实验!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/118576.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!