I/O

I/O

目标

  • 了解 java.io.File 类的主要用途
  • 了解如何使用字节流和字符流
  • 了解如何从文件读取数据和向其中写入数据

处理外部数据

您在 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 的构造方法,只要它是对您的操作系统有效的文件名,无论它引用的文件是否存在。

此代码向新创建的 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 位字符(Reader 和 Writer 类型)。其他流仅能处理 8 位字节(InputStream 和 OutputStream 类型)。这些分层结构中包含多种风格的流,均可在 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 中读取数据。可能最简单的方法是:

在想要从中读取数据的 File 上创建一个 InputStreamReader。 调用 read() 来一次读取一个字符,直至到达文件末尾。 清单 1 是一个从 File 读取的示例:

清单 1. 从 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() 来写入字符序列。

清单 2 是一个将数据写入 File 的例子:

清单 2. 将数据写入到 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 从文件中读取数据,代码类似于 清单 1,但将 InputStreamReader 包装在一个 BufferedReader 中,如清单 3 所示。

清单 3. 使用缓冲的 I/O 从 File 中读取数据

public List<Employee> readFromDiskBuffered(String filename) {
  final String METHOD_NAME = "readFromDisk(String filename)";
  List<Employee> ret = new ArrayList<>();
  File file = new File(filename);
  try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
    String line = reader.readLine();
    int numberOfEmployees = 0;
    while (line != null) {
      StringTokenizer strtok = new StringTokenizer(line, Person.STATE_DELIMITER);
      Employee employee = new Employee();
      employee.setState(strtok);
      log.info("Read Employee: " + employee.toString());
      ret.add(employee);
      numberOfEmployees++;
      // Read next line
      line = reader.readLine();
    }
    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;
}

使用缓冲的 I/O 写入文件的过程相同:将 OutputStreamWriter 包装在一个 BufferedWriter 中,如清单 4 所示。

清单 4. 使用缓冲的 I/O 写入 File

public boolean saveToDiskBuffered(String filename, List<Employee> employees) {
  final String METHOD_NAME = "saveToDisk(String filename, List<Employee> employees)";

  boolean ret = false;
  File file = new File(filename);
  try (BufferedWriter writer = new BufferedWriter(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;
}
上一篇 下一篇