认识IO(输入输出流)

在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流....流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。

Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java中其他多种多样变化的流均由它们派生出来的。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class InputStreamTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        String fileName = "D:/newTemp.txt";
        InputStreamTest.readFileByBytes(fileName);
        InputStreamTest.readFileByChars(fileName);
       
    }

    /**
     * 以字节为单位读取文件,常用于读二进制文件,如图片、声音、影像等文件。
     * 
     * @param fileName
     */
    public static void readFileByBytes(String fileName) {
        File file = new File(fileName);
        InputStream in = null;
        try {
            System.out.println("以字节为单位读取文件内容,一次读一个字节:");
            // 一次读一个字节
            in = new FileInputStream(file);
            int tempbyte;
            while ((tempbyte = in.read()) != -1) {
                System.out.write(tempbyte);
            }
            // 关闭文件输入流
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            System.out.println("以字节为单位读取文件内容,一次读多个字节:");
            // 一次读多个字节,创建一个长度为1024的字节数组来存取
            byte[] tempbytes = new byte[1024];
            // 用于保存实际读取的字节数
            int byteread = 0;
            in = new FileInputStream(fileName);
            // 读入多个字节到字节数组中,byteread为一次读入的字节数
            // 使用循环来进行重复读取
            while ((byteread = in.read(tempbytes)) != -1) {
                System.out.write(tempbytes, 0, byteread);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    // 关闭文件输入流
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 以字符为单位读取文件,常用于读文本,数字等类型的文件
     * 
     * @param fileName
     */
    public static void readFileByChars(String fileName) {
        File file = new File(fileName);
        Reader reader = null;
        try {
            System.out.println("以字符为单位读取文件内容,一次读一个字节:");
            // 一次读一个字符
            reader = new InputStreamReader(new FileInputStream(file), "gbk");
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                // 对于windows下,/r/n这两个字符在一起时,表示一个换行。
                // 但如果这两个字符分开显示时,会换两次行。
                // 因此,屏蔽掉/r,或者屏蔽/n。否则,将会多出很多空行。
                if (((char) tempchar) != '\r') {
                    System.out.print((char) tempchar);
                }
            }
            // 关闭文件输入流
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println("以字符为单位读取文件内容,一次读多个字节:");
            // 一次读多个字符
            char[] tempchars = new char[30];
            int charread = 0;
            reader = new InputStreamReader(new FileInputStream(fileName), "gbk");
            // 读入多个字符到字符数组中,charread为一次读取字符数
            while ((charread = reader.read(tempchars)) != -1) {
                System.out.println(new String(tempchars, 0, charread));
            }

        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    // 关闭文件输入流
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }
}

上面要注意一个问题字节流是根据字节来读取的,而一个中文是占两个字节的,如果包含很多中文的文件被字节流分多次进行读取,可能会造成乱码,因为有可能会导致刚好将一个中文分两次读取,这样就会乱码了,因此如果中文包含多的话还是使用字符流比较好。

在字节流与字符流之间选择的规律如果需要进行输入/输出的内容是文本内容,则应该考虑使用字符流,如果需要进行输入/输出的是二进制内容,则应该考虑使用字节流,因为字节流的功能比字符流强大,计算机中所有的数据都是二进制的,而字节流可以处理所有的二进制文件。

与输入流一样,下面是输出流的演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class OutputStreamTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        OutputStreamTest.writeFileByBytes();
        OutputStreamTest.writeFileByChars();
    }

    /**
     * 以字节为单位输出
     */
    private static void writeFileByBytes() {
        // 创建字节输入流
        FileInputStream fis = null;
        // 创建字节输出流
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("D:/newTemp.txt");
            fos = new FileOutputStream("D:/newTemp2.txt");
            // 一次读多个字节,创建一个长度为40的字节数组来存取
            byte[] bytes = new byte[40];
            // 用于保存实际读取的字节数
            int hasRead = 0;
            // 循环从输入流中读取数据
            while ((hasRead = fis.read(bytes)) != -1) {
                // 每读取一个,即写入文件输出流,读了多少就写多少
                fos.write(bytes, 0, hasRead);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭输入输出流
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 以字符为单位输出
     */
    public static void writeFileByChars() {
        // 创建字符输出
        FileWriter fw = null;
        try {
            fw = new FileWriter("D:/newTemp3.txt");
            fw.write("Hello world!\r\n");
            fw.write("JAVA");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭输出流
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // 同样可以这样
        // 创建字符输出
        OutputStreamWriter osw = null;
        try {
            //创建一个节点输出流
            FileOutputStream fos = new FileOutputStream("D:/newTemp4.txt");
            //以OutputStreamWriter处理流来包装FileOutputStream节点流
            osw = new OutputStreamWriter(fos);
            osw.write("Hello world!\r\n");
            osw.write("Hello world!\r\n");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭输出流
            if (osw != null) {
                try {
                    osw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上面出现了节点流与处理流,区分节点流于处理流的方法是只要流的构造器的参数不是一个物理节点,而是已存在的流,那这个流一定是处理流,因为所有的节点流都是直接以物理io节点作为构造器的参数。

将节点流封装成处理流很简单,只需调用处理流的构造方法来传入节点流就可以了;而且看到上面流的关闭只是关闭了处理流而未去关闭节点流,这样做是完全正确的,以后我们在关闭流的时候只需要关闭最上层的处理流即可。

posted @ 2013-05-28 23:27  百里抱木  阅读(304)  评论(0编辑  收藏  举报