Skip to main content

第七课:数据读写

Java 的流、文件和 IO 是 Java 程序和外部世界交互的基础,用于处理数据的读取和写入操作。例如,从文件读取数据、将数据写入文件、控制台的输入输出等。

java.io 包为这些功能提供了丰富的支持,包括字节流、字符流、文件操作及缓冲流等工具类。

(一)核心概念

1. 流,文件和 I/O

  1. 流(Stream)

    • 数据流是指数据传输的序列。
    • 输入流:从数据源读取数据。
    • 输出流:向目标写入数据。
  2. 文件(File)

    • 提供了对文件和目录的操作支持,如创建、删除、读写等。
  3. I/O(输入与输出)

    • java.io 是处理文件和流操作的基础包,支持二进制数据(如文件、图片)、文本数据和对象序列化。

2. Java I/O 基本分类

  • 字节流 (InputStreamOutputStream):

    • 用于处理二进制数据。例如图像、音频、视频等。

    • 主要类:

      • FileInputStream / FileOutputStream
        • BufferedInputStream / BufferedOutputStream
        • DataInputStream / DataOutputStream
  • 字符流 (ReaderWriter):

    • 用于处理文本数据。

    • 主要类:

      • FileReader / FileWriter
      • BufferedReader / BufferedWriter

(二)控制台输入输出

1. 控制台输入

使用 BufferedReader

需要将 System.in 包装成 BufferedReader,然后使用 read()readLine() 方法读取输入。

读取单个字符

import java.io.*;

public class BRRead {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        char c;

        System.out.println("输入字符,按下 'q' 键退出:");

        // 逐个读取字符
        do {
            c = (char) br.read();
            System.out.println(c);
        } while (c != 'q');
    }
}

执行结果

输入字符,按下 'q' 键退出:
r
r
u
u
q
q

代码解释:

throws IOException 用于声明的异常通常出现在流操作时(例如输入输出时的错误)

System.in:标准输入流,默认为控制台键盘输入。

InputStreamReaderSystem.in 的输入数据从字节转换成字符。

BufferedReader 对字符流提供缓冲功能,允许使用 read()readLine() 等方法读取字符或行。

读取一整行字符串

使用 readLine() 方法读取整行的输入直到按回车为止。

import java.io.*;

public class BRReadLines {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str;

        System.out.println("输入文本行,输入 'end' 退出:");

        // 循环读取字符串
        do {
            str = br.readLine();
            System.out.println(str);
        } while (!str.equals("end"));
    }
}

执行结果

输入文本行,输入 'end' 退出:
Hello World
Hello World
end
end

使用 scanner 读取输入 和 使用 BufferedReader 的区别:

特性ScannerBufferedReader
所在包java.utiljava.io
解析功能内置数据类型解析(如nextInt()仅支持字符串读取(需手动解析数据类型)
读取单行/多行支持支持逐词(带分隔符)和整行读取多用于逐行读取
读取效率较慢,适合小数据量输入(开销较高)高效,适合大数据量读取(缓冲读取)
线程安全性非线程安全非线程安全
数据分隔自动按空格制表符换行分隔不自动分隔,仅按行读取
错误处理/异常抛出InputMismatchException等运行时异常抛出IOException编译时异常
适用场景小数据量的用户输入和格式化数据处理从文件或控制台读取大量数据

2. 控制台输出

System.outPrintStream 的对象引用,支持 print()println() 和低级的字节写操作 write()

public class WriteDemo {
    public static void main(String[] args) {
        int b = 'A'; // 字符 'A'
        System.out.write(b); // 写入单个字符
        System.out.write('\n'); // 换行
    }
}

输出结果

A

(三) 读写文件

Java 可通过操作文件流来实现文件的读写,字节流类 (FileInputStream / FileOutputStream) 和字符流类 (FileReader / FileWriter) 是主要工具。

1. 使用字节流

  • 读取文件 (FileInputStream)

用于从文件中读取数据,字节流适合处理二进制文件。

import java.io.*;

public class FileInputStreamDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("test.txt");
            int ch;

            while ((ch = fis.read()) != -1) { // 循环读取字节。文件读完 read() 返回 -1。
                System.out.print((char) ch); // 强转为字符输出
            }

            fis.close(); // 关闭流
        } catch (IOException e) {  // 处理 IOException 异常。
            e.printStackTrace();
        }
    }
}
  • 写入文件 (FileOutputStream)

将数据以字节形式写入文件。若文件不存在,系统会自动创建。

import java.io.*;

public class FileOutputStreamDemo {
    public static void main(String[] args) {
        try {
            FileOutputStream fos = new FileOutputStream("test.txt");
            String content = "Hello, world!";

            fos.write(content.getBytes()); // 写入内容
            fos.close(); // 关闭流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Java 的工作目录: Java 程序的当前工作路径。

在程序运行时,Java 默认会使用当前工作目录作为相对路径的基准。

获取当前工作目录

System.out.println("当前工作目录:" + System.getProperty("user.dir"));

中文编码问题:

默认情况下,FileInputStream 读取的是原始字节流,并没有考虑字符编码,所以如果文件包含非 ASCII 字符(如中文),会出现乱码或 ?

你可以使用 InputStreamReader 来处理字符编码, 这是一个桥接流,用来将字节流转换成字符流。

在构造时,你可以指定字符编码(如 "UTF-8""GBK"),这会确保字符被正确地解码。

把上面的

FileOutputStream fos = new FileOutputStream("test.txt");

改成

InputStreamReader fos = new InputStreamReader(new FileInputStream("test.txt"), "UTF-8");

2. 使用字符流

使用 BufferedReaderBufferedWriter 读写文件

import java.io.*;

public class BufferedReaderWriterExample {
    public static void main(String[] args) throws IOException {
        // 写入文件
        BufferedWriter writer = new BufferedWriter(new FileWriter("example.txt"));
        writer.write("Hello, World!");
        writer.newLine(); // 换行
        writer.write("This is Java IO.");
        writer.close();

        // 读取文件
        BufferedReader reader = new BufferedReader(new FileReader("example.txt"));
        String line;

        while ((line = reader.readLine()) != null) { // 逐行读取
            System.out.println(line);
        }

        reader.close();
    }
}

输出结果

Hello, World!
This is Java IO.

(四)Java 文件操作

1. 创建目录

使用 File 类的 mkdir()mkdirs() 方法可以分别创建单级和多级目录。

import java.io.*;

public class CreateDirectory {
    public static void main(String[] args) {
        File directory = new File("demo/newdir");
        boolean result = directory.mkdirs(); // 创建目录
        if (result) {
            System.out.println("目录已创建!");
        } else {
            System.out.println("目录创建失败!");
        }
    }
}

2. 读取目录内容

使用 File 对象的 list() 方法获取目录列表。

import java.io.*;

public class ReadDirectory {
    public static void main(String[] args) {
        File directory = new File("/tmp");
        if (directory.isDirectory()) {
            String[] files = directory.list();
            for (String file : files) {
                System.out.println(file);
            }
        } else {
            System.out.println("不是一个目录!");
        }
    }
}

3. 删除目录或文件

使用 File.delete() 方法删除文件或空目录。

import java.io.*;

public class DeleteFile {
    public static void main(String[] args) {
        File file = new File("demo/test.txt");
        if (file.delete()) {
            System.out.println("文件已删除!");
        } else {
            System.out.println("文件删除失败!");
        }
    }
}

对于非空目录,需要递归删除。