说说 Java I/O 系统之 I/O 流的典型用法(带示例)

下面将要说到的这些示例,所有的异常处理都被简化给了控制台。在实际项目中,建议加入更复杂的错误处理能力。

1 读取文件(带缓冲)

如果要打开一个文件,从文件中读取字符,那么可以使用 FileInputReader。但因为 使用 FileInputReader 读取字符时,会造成中文乱码。所以我们这里采用 FileInputStream,然后利用 InputStreamReader 设置编码格式。最后为了提高处理速度,这里把 InputStreamReader 的引用传给了 BufferedReader 构造器。因为 BufferedReader 也有 readLine() 方法,这样当 readLine() 返回 null 时,就说明已经到了这个文件的末尾,那么读取终止:

public class BufferedInputFile {
    // Throw exceptions to console:
    public static String read(String filename) throws IOException {
        // Reading input by lines:
        BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream
                (filename), "UTF-8"));


        String s;
        StringBuilder sb = new StringBuilder();
        while ((s = in.readLine()) != null)
            sb.append(s + "\n");
        in.close();
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {
        System.out.println(read("xxx"));
    }
}

2 读取文件到内存

使用 BufferedInputFile.read() 的返回字符串,作为 StringReader 构造器的参数。然后调用 read() 每次读取一个字符,最后发送给控制台:

public class MemoryInput {
    public static void main(String[] args) throws IOException {
        StringReader in = new StringReader(BufferedInputFile.read("xxx"));
        int c;
        while ((c = in.read()) != -1)
            System.out.println((char) c);

    }
}

3 读取字节数据到内存

这里使用 DataInputStream,因为它是一个面向字节的类,所以我们这里用的是 InputStream 类,这个类可以以字节的形式读取任何数据:

public class FormattedMemoryInput {
    public static void main(String[] args) throws IOException {
        try {
            DataInputStream in = new DataInputStream(new ByteArrayInputStream
                    (BufferedInputFile.read("xxx").getBytes()));
            while (true)
                System.out.print((char) in.readByte());
        } catch (EOFException e) {
            System.err.println("End of stream");
        }
    }
}

下面演示了一次一个字节地读取文件,并检测输入是否结束的方法:

public class TestEOF {

    public static void main(String[] args) throws IOException {
        DataInputStream in = new DataInputStream(new BufferedInputStream(new
                FileInputStream("xxx")));
        while (in.available() != 0) {
            System.out.print((char) in.readByte());
        }
    }
}

这里使用了 available() 方法查看还有多少可供存取的字符,注意 available() 的工作方法会随着读取的媒介类型的不同而不同。对于文件,指的是整个文件;如果是其他类型的流,可能不是这样,所以要谨慎使用!

4 写入文件

FileWriter 可以向文件写入数据,但会造成中文乱码,所以我们改用 OutputStreamWriter+FileOutputStream 的方式写入文件,我们还使用了 BufferedReader 将其包装起来以便提供缓冲输出的能力(缓冲会显著地增加 I/O 操作的性能)。我们使用 PrintWriter 以便提供格式化的能力:

public class BasicFileOutput {
    static String file = "D:\\temp\\1\\1.txt";

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read
                ("D:\\temp\\1\\2.txt")));
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new
                FileOutputStream(file), "UTF-8")));

        int lineCount = 1;
        String s;
        while ((s = in.readLine()) != null)
            out.println(lineCount++ + ":" + s);//记录行号
        out.close();

        // Show the stored file:
        System.out.println(BufferedInputFile.read(file));
    }
}

Java SE5 为 PrintWriter 添加了一个辅助构造器,这样我们就可以不必添加装饰器而直接写入文件啦:

public class FileOutputShortcut {
    static String file = "D:\\temp\\1\\1.txt";

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read("D:\\temp\\1\\2.txt")));

        // Here's the shortcut:
        PrintWriter out = new PrintWriter(file);
        int lineCount = 1;
        String s;
        while ((s = in.readLine()) != null)
            out.println(lineCount++ + ":" + s);
        out.close();
        // show the stored file:
        System.out.println(BufferedInputFile.read(file));
    }
}

可惜的是,其他常见的写入任务都没有这样的快捷方式,所以典型的 I/O 仍然会写的很长(因为加入了多个装饰器类,以便提供相应的能力)。

5 写入和读取数据

使用 DataOutputStream 写入数据,然后用 DataInputStream 读取数据,因为 DataOutputStream 和 DataInputStream 都是面向字节的,所以这里使用的是 InputSteam 和 OutputStream:

public class StoringAndRecoveringData {
    public static void main(String[] args) throws IOException {
        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new
                FileOutputStream("D:\\temp\\1\\Data.txt")));

        out.writeDouble(3.14159);
        out.writeUTF("That was pi");
        out.writeDouble(1.41413);
        out.writeUTF("Square root of 2");
        out.close();

        DataInputStream in = new DataInputStream(new BufferedInputStream(new
                FileInputStream("D:\\temp\\1\\Data.txt")));
        System.out.println(in.readDouble());

        // Only readUTF() will recover the Java-UTF String properly:
        System.out.println(in.readUTF());
        System.out.println(in.readDouble());
        System.out.println(in.readUTF());

    }
}

使用 DataOutputStream 写入的数据,都可以用 DataInputStream 准确地读取数据,这可以在跨平台的系统中使用。但我们必须使用 UTF-8 编码,所以在上述示例中,我们读写字符串用的是 writeUTF() 和 readUTF() 方法!UTF-8 将 ASCII 字符编码成单一字节的格式,非 ASCII 字符被编码为两到三个字节的格式,这样可以节省空间和带宽。字符串的长度存储在 UTF-8 字符串的前两个字节中。因为 writeUTF() 和 readUTF() 使用的是适用于 Java 的 UTF-8 变体,所以如果用一个非 Java 程序读取用 writeUTF() 写入的字符串时,需要编写一些特殊代码才能正确读取。

writeDouble() 会将 double 类型的数字存储到流中,然后再用 readDouble() 读取它。其他基本类型也有类似的方法可以读写。为了保证读取数据准确无误,我们必须知道流中的数据项的确切位置。因此,要么把文件中的数据定义为固定格式,要么将额外的信息保存在文件中,这样才能够正确解析以便确定数据的存放位置。所以,对象序列化和 XML 可能是更容易读写的、复杂的数据结构!

6 读写随机访问文件

在 RandomAccessFile 类中,使用 seek() 可以在文件中移动,并修改文件某处的值。

必须事先知道文件的排版内容,才能正确地操作它。在 RandomAccessFile 类中,有读取基本类型和 UTF-8 字符串的各种方法:

public class UsingRandomAccessFile {
    static String file = "rtest.dat";

    static void display() throws IOException {
        RandomAccessFile rf = new RandomAccessFile(file, "r");
        for (int i = 0; i < 7; i++) {
            System.out.println("Value " + i + ":" + rf.readDouble());
        }
        System.out.println(rf.readUTF());
        rf.close();
    }

    public static void main(String[] args) throws IOException {
        RandomAccessFile rf = new RandomAccessFile(file, "rw");
        for (int i = 0; i < 7; i++) {
            rf.writeDouble(i * 1.414);
        }
        rf.writeUTF("The end of the file");
        rf.close();
        display();

        rf = new RandomAccessFile(file, "rw");
        rf.seek(5 * 8);//寻找第 5 个 double 类型的值(double 类型为 8 字节长度)
        rf.writeDouble(47.0001);
        rf.close();
        display();
    }
}

你可能感兴趣的:(Java)