Java Basics Part 18/20 - Files and I/O
目录
java.io 包几乎包含了操作 I/O 所需要的所有类。所有的这些流都代表了一个输入和一个输出。java.io 包支持很多的数据类型,包括原始数据类型,对象,本地化字符等等。
Streams
可以认为流(Streams)就是一串数据序列。有两种形式:
- 输入流:输入流用来从 source 中读取数据。
- 输出流:输出流用来往 destination 中写入数据。
Java 为文件和网络提供了功能非常强大和灵活的支持。但是这里只介绍基本的流和 I/O。下面逐一介绍最常用的一些流。
Byte Streams
Java 的字节流是用来操作 8-bit 字节的输入输出的。虽然有很多类都和字节流有关系,但是最常用的两个类是 FileInputStream 和 FileOutputStream。下面这个示例演示了把一个文件的内容拷贝到另一个文件中。
import java.io.*;
public class CopyFile {
public static void main(String args[]) throws IOException
{
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("input.txt");
out = new FileOutputStream("output.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
现在我们在 input.txt 文件中有以下内容:
This is test for copy file.
按照如下步骤编译然后运行,会得到一个 output.txt 文件,它的内容和 input.txt 是一样的。
$javac CopyFile.java
$java CopyFile
Character Streams
Java 的字节流用来操作 8-bit 字节的输入和输出的,而 Java 的字符流(Character Streams)用来操作 16-bit unicode 的输入和输出。虽然有很多类都和字符流有关系,但是最常用的两个类是 FileReader 和 FileWriter。虽然在底层 FileReader 还是调用的 FileInputStream,FileWriter 还是调用的 FileOutputStream,但是这里还是有一个很大的差别:
FileReader 一次读入两个字节,FileWriter 一次写入两个字节
重写上述的例子,使用字符流来完成文件(内含 unicode 字符)的内容拷贝。
import java.io.*;
public class CopyFile {
public static void main(String args[]) throws IOException
{
FileReader in = null;
FileWriter out = null;
try {
in = new FileReader("input.txt");
out = new FileWriter("output.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
编译运行与上一节相同。
Standard Streams
所有的编程语言都提供了标准 I/O,也就是用户可以从键盘读取输入并且把输出显示到屏幕上。如果接触过 C/C++ 的话,那么一定知道三个标准设备 STDIN, STDOUT 和 STDERR。与此相似,Java 也提供了如下三个标准流:
- Standard Input: 用来为程序提供数据,通常键盘用作标准输入流,Java 中用 System.in 表示。
- Standard Output: 用来为程序输出数据,通常电脑屏幕用作标准输出流,Java 中用 System.out 表示。
- Standard Error: 用来为程序输出错误数据,通常电脑屏幕用作标准错误流,Java 中用 System.err 表示。
下面一个简单的示例,创建了一个 InputStreamReader 用来读取标准输入流,直到用户输入 “q”:
import java.io.*;
public class ReadConsole {
public static void main(String args[]) throws IOException
{
InputStreamReader cin = null;
try {
cin = new InputStreamReader(System.in);
System.out.println("Enter characters, 'q' to quit.");
char c;
do {
c = (char) cin.read();
System.out.print(c);
} while(c != 'q');
}finally {
if (cin != null) {
cin.close();
}
}
}
}
编译运行:
$javac ReadConsole.java
$java ReadConsole
Enter characters, 'q' to quit.
1
1
e
e
q
q
Reading and Writing Files
如前所述,流是一串数据序列。InputStream 用来从 source 中读取数据, OutputStream 往 destination 中写入数据。
下图是 Input 和 Output 流的类结构图。
两个重要的流是 FileInputStream 和 FileOutputStream,下面就来介绍下这两个类。
FileInputStream
这个流用来从文件中读取数据。这个类有很多的构造器。
如下构造器接收一个字符串类型的形参作为文件名,并且基于这个文件创建一个输入流:
InputStream f = new FileInputStream("C:/java/hello");
如下这个构造器结构一个 File 对象,基于这个文件创建一个输入流,首先要使用 File() 创建一个 file 对象:
File f = new File("C:/java/hello");
InputStream f = new FileInputStream(f);
一旦持有了 InputStream,那么就有很多操作 流 的方法可以使用了。
SN | Methods with Description |
---|---|
1 | public void close() throws IOException{}: 关闭资源。 |
2 | protected void finalize()throws IOException {}:清除 文件连接。 |
3 | public int read()throws IOException{}:读取 1 个字节的数据。 |
4 | public int read(byte[] r) throws IOException{}:读取 r.length 个字节的数据到数组里。 |
5 | public int available() throws IOException{}:返回文件输入流中还有多少字节数据可读。 |
还有其他一些很重要的输入流,详情请点击:
FileOutputStream
FileOutputStream 创建一个可以写入数据的文件。如果文件不存在,会创建一个。
下面是它的两个构造器。
第一个构造器接收一个字符串类型的文件名称做参数,基于这个文件创建一个输出流:
OutputStream f = new FileOutputStream("C:/java/hello")
第二个构造器接收一个文件对象做参数,然后写入这个文件对象指向的文件:
File f = new File("C:/java/hello");
OutputStream f = new FileOutputStream(f);
一旦持有了 OutputStream,那么就有很多操作流的方法可以使用了。
SN | Methods with Description |
---|---|
1 | public void close() throws IOException{}: 关闭流 |
2 | protected void finalize()throws IOException {}:确保清除文件链接 |
3 | public void write()throws IOException{}:写入一个字节 |
4 | public void write(byte[] w):写入整个字节数组 |
还有其他的输出流可供使用,详情请点击:
举例:
演示 InputStream 和 OutputStream
import java.io.*;
public class fileStreamTest{
public static void main(String args[]){
try{
byte bWrite [] = {11,21,3,40,5};
OutputStream os = new FileOutputStream("test.txt");
for(int x=0; x < bWrite.length ; x++){
os.write( bWrite[x] ); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for(int i=0; i< size; i++){
System.out.print((char)is.read() + " ");
}
is.close();
}catch(IOException e){
System.out.print("Exception");
}
}
}
上面这个例子创建出 test.txt 文件,然后以二进制格式写入一串给定的数字。相同的内容会出现在 stdout 上。
File Navigation and I/O
还有一些其他的操作 File 的类可以学习:
Directories in Java
可以使用 File 对象来 创建目录, 列出目录中的文件 等等。
Creating Directories
有两个很有用的 File 工具方法,都可以用来创建目录:
- mkdir():返回 boolean。如果 路经 存在,那么返回失败。
- mkdirs():递归创建文件夹
下面这个实例创建了 “/tmp/user/java/bin” 目录
import java.io.File;
public class CreateDir {
public static void main(String args[]) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// Create directory now.
d.mkdirs();
}
}
注意路径分隔符,Unix 和 Windows 都可以处理 “/”
Listing Directories
可以使用 File 对象的 list() 方法来列出文件夹中的所有文件:
import java.io.File;
public class ReadDir {
public static void main(String[] args) {
File file = null;
String[] paths;
try{
// create new file object
file = new File("/tmp");
// array of files and directory
paths = file.list();
// for each name in the path array
for(String path:paths)
{
// prints filename and directory name
System.out.println(path);
}
}catch(Exception e){
// if any error occurs
e.printStackTrace();
}
}
}
这个程序会列出 /tmp 文件夹中的所有目录和文件:
test1.txt
test2.txt
ReadDir.java
ReadDir.class