集册 Java 精简入门教程 Java I/O与流读写文件

Java I/O与流读写文件

欢马劈雪     最近更新时间:2020-08-21 11:12:43

484

I/O

本节将概述 java.io 包。您将学习如何使用它的一些工具来收集和操作各种不同来源的数据。

处理外部数据

在 Java 程序中使用的数据常常来自外部数据源,比如数据库、通过套接字进行的直接字节传输或文件存储。Java 语言提供了许多工具从这些来源获取信息,其中大部分工具都位于 java.io 包中。

文件

在所有 Java 应用程序可用的数据源中,文件是最常见的,常常也是最方便的。如果想在 Java 应用程序中读取一个文件,必须使用 将传入的字节解析为 Java 语言类型。

java.io.File 是一个类,它在您的文件系统上定义资源并以一种抽象的方式表示该资源。创建 File 对象很容易:

File f = new File("temp.txt");
File f2 = new File("/home/steve/testFile.txt");

File 构造函数接受它所创建的文件的名称。第一个调用在指定的目录中创建一个名为 temp.txt 的文件。第二个调用在我的 Linux 系统上的具体位置创建一个文件。您可将任何 String 传递至 File 的构造函数,只要文件名对 OS 而言是有效的,无论它引用的文件是否存在都是如此。

此代码向新创建的 File 对象询问该文件是否存在:

File f2 = new File("/home/steve/testFile.txt");
if (f2.exists()) {
  // File exists.Process it...
} else {
  // File doesn't exist.Create it...
  f2.createNewFile();
}

java.io.File 拥有其他方便的方法来:

  • 删除文件
  • 创建目录(通过将一个目录名称作为参数传递至 File 的构造函数)
  • 确定一个资源是文件、目录还是符号链接
  • 等等

Java I/O 的实际操作是写入和读取数据源,这时就需要使用流。

在 Java I/O 中使用流

可以使用流来访问文件系统上的文件。在最低限度上,流允许程序从来源接收字节或将输出发送至目标。一些流可处理所有类型的 16 位字符( ReaderWriter 类型)。其他流只能处理 8 位字节( InputStreamOutputStream 类型)。这些分层结构中包含多种风格的流,它们都可在 java.io 包中找到。在最高的抽象级别上是 字符流字节流

字节流读( InputStream 和子类)和写( OutputStream 和子类)8 位字节。换句话说,可将字节流看作一种更加原始的流类型。下面总结了两种常见的字节流及其用法:

  • FileInputStream / FileOutputStream : 从文件读取字节,将字节写入文件
  • ByteArrayInputStream / ByteArrayOutputStream :从内存型数组读取字节,将字节写入内存型数组

字符流

字符流读(Reader 和它的子类)和写(Writer 和它的子类)16 位字符。下面是一个字符流清单及其用法:

  • **StringReader** / **StringWriter**:在内存中的 String 中读取以及向其中写入字符。
  • **InputStreamReader** / **InputStreamWriter** (和子类 **FileReader** / **FileWriter**):衔接字节流和字符流。Reader 喜欢从字节流读取字节并将其转换为字符。Writer 喜欢将字符转换为字节,从而将它们放在字节流上。
  • **BufferedReader** / **BufferedWriter**:在读取或写入另一个流时缓冲数据,使读写操作更高效。

我不会介绍所有流,而是主要介绍读写文件时推荐使用的流。在大多数情况下,这些都是字符流。

从 File 读取数据

可通过多种方式从 File 读取数据。无疑最简单的方法是:

  1. 在想要从中读取数据的 File 上创建一个 InputStreamReader
  2. 调用 read() 可一次读取一个字符,直至到达文件末尾。

清单 8 是一个从 File 读取数据的示例:

清单 8. 从 File 读取数据
public List<Employee> readFromDisk(String filename) {
  final String METHOD_NAME = "readFromDisk(String filename)";
  List<Employee> ret = new ArrayList<>();
  File file = new File(filename);
  try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file))) {
    StringBuilder sb = new StringBuilder();
    int numberOfEmployees = 0;
    int character = reader.read();
    while (character != -1) {
        sb.append((char)character);
        character = reader.read();
    }
    log.info("Read file:\n" + sb.toString());
    int index = 0;
    while (index < sb.length()-1) {
      StringBuilder line = new StringBuilder();
      while ((char)sb.charAt(index) != '\n') {
        line.append(sb.charAt(index++));
      }
      StringTokenizer strtok = new StringTokenizer(line.toString(), Person.STATE_DELIMITER);
      Employee employee = new Employee();
      employee.setState(strtok);
      log.info("Read Employee:" + employee.toString());
      ret.add(employee);
      numberOfEmployees++;
      index++;
    }
    log.info("Read " + numberOfEmployees + " employees from disk.");
  } catch (FileNotFoundException e) {
    log.logp(Level.SEVERE, SOURCE_CLASS, METHOD_NAME, "Cannot find file " +
       file.getName() + ", message = " + e.getLocalizedMessage(), e);
  } catch (IOException e) {
    log.logp(Level.SEVERE, SOURCE_CLASS, METHOD_NAME, "IOException occurred,
       message = " + e.getLocalizedMessage(), e);
  }
  return ret;
}

写入 File

与从 File 读取一样,可通过多种方式将数据写入 File。我会再次介绍一下最简单的方法:

  1. 在想要写入数据的 File 上创建一个 FileOutputStream
  2. 调用 write() 写入字符序列。

清单 9 是一个将数据写入 File 的示例:

清单 9. 写入 File
public boolean saveToDisk(String filename, List<Employee> employees) {
  final String METHOD_NAME = "saveToDisk(String filename, List<Employee> employees)";

  boolean ret = false;
  File file = new File(filename);
  try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file))) {
    log.info("Writing " + employees.size() + " employees to disk (as String)...");
    for (Employee employee : employees) {
      writer.write(employee.getState()+"\n");
    }
    ret = true;
    log.info("Done.");
  } catch (FileNotFoundException e) {
    log.logp(Level.SEVERE, SOURCE_CLASS, METHOD_NAME, "Cannot find file " +
       file.getName() + ", message = " + e.getLocalizedMessage(), e);
  } catch (IOException e) {
    log.logp(Level.SEVERE, SOURCE_CLASS, METHOD_NAME, "IOException occurred,
       message = " + e.getLocalizedMessage(), e);
  }
  return ret;
}

缓冲流

逐个字符地读和写字符流并不是高效的,所以在大部分情况下,您可能希望使用缓冲的 I/O。要使用缓冲的 I/O 从文件读取数据,代码与清单 28 中所示的类似,但要将 InputStreamReader 包装在一个 BufferedReader 中,如清单 10 所示。

清单 10. 使用缓冲的 I/O 从 File 读取数据
展开阅读全文