IO总结1:字符流

------- android培训java培训、期待与您交流! ----------

image

上表是IO中主要的类的继承体系结构。

包含Stream的都是操作字节流的。包括InputerStream(表示输入流)和OutputStream(表示输出流)。

包含Reader或Writer的都是操作字符流的,Reader表示输入流,Writer表示输出流。

同时有Stream和Reader/Writer的则有一个转换动作。因为字节对于人来说难以理解,而字符对人来说便于理解。

InputStreamReader是字节流通向字符流的桥梁。因为在计算机中(包括内存、硬盘等)都是2进制来表示数据,那么读取的时候都需要把字节流转换成字符流。

OutputStreamWriter是字符流通向字节流的桥梁。把人易于理解的字符存储到计算的内存或是硬盘中时,需要把字符流转成字节流。

所以不会存在InputStreamWriter和OutputStreamReader。从另一个方面来说,Input和Reader表示输入,Output和Writer表示输出,如过输入和输出在一个方法中,那么这个方法到底是输入呢?还是输出,就会造成疑惑。除了RandomAccessFile这个类,其他的类都只具备输入或输出功能。

首先最先使用的是FileWriter,如上表所述,这个类继承了OutputStreamWriter,这个类将字符流转成字节流,保存到文件中。

输入和输出通常是程序外部的,就会出现不符合期望的情况发生,所以可能会出现大量的异常。(有几个类不会抛出异常,后面再说)。

所以在使用的时候,需要捕获和处理异常。一个新建文件的例子:

import java.io.*;
class FileWriteDemo {
    public static void main(String[] args) {
        FileWriter fw = new FileWriter("d:/abc.txt");
        fw.write("Hellow World!");
        fw.close();
    }
}

短短的3句话,就有可能出现很多异常,比如说,无法创建文件(权限不足、指定的位置无法访问等)。而这段代码是不能被编译通过的,因为函数没有对可能抛出的异常进行处理,或者是抛出异常。要想编译通过,要在

public static void main(String[] args)//后面加上throws IOException

异常的经典处理方法:

import java.io.*;
class FileWriteDemo {
    public static void main(String[] args) {
        // 在这里定义fw变量为null,使其在finally语句中也能使用fw变量
        FileWriter fw = null;
        try {
            // 如果try前面不定义变量,而在这里写为FileWriter new FileWriter("d:/abc.txt");
            // 那么finally语句中则不能使用fw变量
            fw = new FileWriter("d:/abc.txt");
            fw.write("Hellow World!");
        }
        catch (IOException e) {
            // 或者写成throw new RuntimeException("创建文件失败");
            System.out.println("catch:" + e.toString());
        }
        finally {
            try {
                // 如果创建文件失败,fw还是null,调用close方法则会导致空指针异常,所以要先判断fw是否为空
                if (fw != null)
                    fw.close();
            }
            catch (IOException e) {
                System.out.println("catch:" + e.toString());
            }
        }
    }
}

值得注意的是,在创建FileWrite对象的时候如果用的是

FileWriter fw = new FileWrite(“abc”);

,如果文件不存在,则会创建,如果存在将会覆盖。所以也有一个追加模式,也就是新的内容写在现有内容的后面:

FileWriter fw = new FileWrite(“abc”,true);

 

说完了FileWriter,现在说一下FileReader:

FileReader用于读取文件。

FileReader的read方法都是继承过来的:

Reader类中有4个read方法:

1、Read(),一次读一个,返回读取到的字符的整数形式,所以需要将返回值转为char类型, 到末尾,返回-1

2、read(char[] cbuf),将读到的字符传入字符数组,长度为数组的长度,返回值为读取到的字符数,到末尾,返回-1

3、read(char[] cbuf, int offset, int length)抽象方法,需要子类实现。

4、read(CharBuffer target) CharBuffer没学,不用这个方式。

InputStreamReader有2个read方法:

1、Read(),一次读一个,返回读取到的字符的整数形式,所以需要将返回值转为char类型, 到末尾,返回-1

2、read(char[] cbuf, int offset, int length) 返回int为读取的字符数。这个方法将数据存放在事先定义好的cbuf字符数组中,读取的长度由length决定,将读取出来的字符,一个一个放在cbuf数组的设定的起始位中。注意,offset+length不能超过cbuf的length。否则会报错。

方法1:使用无参数read方法

import java.io.*;
class FileReaderDemo1 {
    // 便于简化代码,未对异常进行处理,直接由主函数抛出
    public static void main(String[] args) throws IOException {
        // 建立对象,指定需要读取的文件
        FileReader fr = new FileReader("d:/abc.txt");
        // FileReader的方法都是继承过来的,其中,从InputStreamReader类中继承了read方法
        // 对于没有参数的read方法,每次只读单个字符,且返回值为int,如果读取到文件末尾,read方法返回-1
        // 所以采用while循环的方式进行如下处理,
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.print((char) ch);
        }
        fr.close();
    }
}

方法2:使用了Reader中的read(char[] cbuf)方法

import java.io.*;
class FileReaderDemo2 {
    // 便于简化代码,未对异常进行处理,直接由主函数抛出
    public static void main(String[] args) throws IOException {
        // 建立对象,指定需要读取的文件
        FileReader fr = new FileReader("d:/abc.txt");
        // 事先定义好一个字符型数组,准备用于存放读取到的字符。长度一般为1024的整数倍。
        char[] charBuf = new char[5];
        int len; // 使用Reader中的read(charbuf)方法,将read的返回值存入len;
        while ((len = fr.read(charBuf)) != -1) {
            // 因为文件中的字符数可能不是我们指定的字符数组的长度的整数倍,
            // 余下的字符将存入字符数组的开头,而字符数组尾端的内容则没有被覆盖。
            // 所以没有使用String类中的构造函数String(char[] value)
            // 而是使用String类中的构造函数String(char[] value, int offset, int count)
            System.out.println(new String(charBuf, 0, len));
        }
        fr.close();
    }
}

 

读取和写入都搞定之后,可以尝试复制一个文本型的文件到另外一个地方:

import java.io.*;
class CopyTextDemo {
    // 便于程序简短,没有处理异常。
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:/abc.txt");
        FileWriter fw = new FileWriter("E:/abc.txt");
        char[] charBuf = new char[1024];
        int len;
        while ((len = fr.read(charBuf)) != -1) {
            fw.write(charBuf, 0, len);
        }
        fr.close();
        fw.close();
    }
}

上面的例子中可以看出,FileReader的一个较大弊端就是一个字符一个字符地读,但对于人来说,行才是比较直观的。所以出现了BufferedReader类,这个类可以一次读取一行。这个类是一个装饰类是Reader类的子类,可以增强FileReader的功能,具体体现在一次可以读取一行。虽然可以自定义类来增强FileReader,但由于多平台对于换行的处理方式不同,所以这个类的出现,简化了代码,提高了编程的高效性。

BufferedReader在创建对象的时候需要将被增强的字符流对象传入:

BufferedReader bufr = new BufferedReader (new FileReader(“abc.txt”));

并使用readLine的方法读取一行,值得注意的是和FileReader不同当读取到文件末尾的时候,返回值不再是-1而是null。另外一个值得注意的是,readLine返回的字符串是不包含换行符的。

下面是一个读取一个文件,并打印在终端上的一个例子:

import java.io.*;
class BufferedReadDemo {
    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("D:/abc.txt");
        BufferedReader bufr = new BufferedReader(fr);
        String line = null;
        while ((line = bufr.readLine()) != null) {
            System.out.println(line);
            // 每次打印完一行,主线程休眠0.1秒,可以看出读取之后就刷新了流。
            Thread.currentThread().sleep(100);
        }
        bufr.close();
    }
}

 

另外一个例子就是利用BufferedReader和BufferedWriter复制文本文件的例子:

import java.io.*;
class BufferedTextCopy {
    public static void main(String[] args) throws IOException {
        BufferedReader bufr = new BufferedReader(new FileReader("D:/abc.txt"));
        BufferedWriter bufw = new BufferedWriter(new FileWriter("E:/abc.txt"));
        String line = null;
        while ((line = bufr.readLine()) != null) {
            bufw.write(line);
            bufw.newLine();
        }
        bufr.close();
        bufw.close();
    }
}

比较遗憾的是:如果原始文件最后一行没有换行符的话,在目的文件中却多了一个空行。没有达到100%原样copy。

BufferedWriter下没有子类,而BufferedReader下有一个子类LineNumberReader,可以调用对象的方法getLineNumber()获取到所在行,特殊情况下有一定的用途。虽然也可在BufferedReader读取文件的while循环中加入计数器(定义计数器要加在while循环上)来实现这一效果。就不举例了。LineNumberReader也可以算一个装饰类。

posted @ 2012-12-29 00:35  qinbin  阅读(329)  评论(0编辑  收藏  举报