I/O篇(3)——各I/O类的用法

来源:互联网 发布:zuk z2 网络较慢 编辑:程序博客网 时间:2024/06/05 17:39

一.常用节点流

\ InputStream OutputStream Reader Writer 文件 FileInputStream FileOutputStrean FileReader FileWriter 数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 字符串 - - StringReader StringWriter 管道 PipedInputStream PipedOutputStream PipedReader PipedWriter

1.FileInputStream/FileOutputStream(FileReader/FileWriter类似,所以只给出一组类的例子)
举例:用FileInputStream/FileOutputStream实现文件的拷贝

public static void copyFile(String srcPath,String destPath) {        //建立源文件(存在且为文件)        File src = new File(srcPath);        //建立目的文件(文件可以不存在)        File dest = new File(destPath);        if(!src.isFile()){            throw new RuntimeException("给定的源不是文件");        }        InputStream is = null;        OutputStream os = null;        try {            is = new FileInputStream(src);            os = new FileOutputStream(dest);            byte[] temp = new byte[1024];            int len = 0;            //循环读取,写出            while(-1 != (len = is.read(temp, 0, temp.length))){                os.write(temp, 0, len);            }            //结束前刷新,把缓冲中的内容全部写出            os.flush();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //关闭流            try {                if(null != os){                    os.close();                }                if(null != is){                    is.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }

2.ByteArrayInputStream/ByteArrayOutputStream(CharArrayReader/CharArrayWriter类似,所以只给出一组类的例子)(有新增方法,一般不用多态)以数组为物理节点的节点流,字节流以字节数组为节点ByteArrayInputStream/ByteArrayOutputStream,字符流以字符数组为节点CharArrayReader/CharArrayWriter, 这种数组流除了在创建节点流需要传入数组外,用法上与其他节点流完全类似.数组流类似还可以使用字符串作为物理节点,用于实现从字符串中读取字符串(String)java.io.StringReader, 或将内容写入字符串(StringBuffer)java.io.StringWriter.
(1)把文件写入字节数组

public static byte[] readFile2ByteArray(String srcPath) {        File src = new File(srcPath);        InputStream is = null;        ByteArrayOutputStream bos = null;        byte[] dest = null;        try {            is = new BufferedInputStream(new FileInputStream(src));            bos = new ByteArrayOutputStream();            byte[] temp = new byte[1024];            int len = 0;            while (-1 != (len = is.read(temp))) {                bos.write(temp, 0, len);            }            bos.flush();            dest = bos.toByteArray();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if(null != bos){                    bos.close();                }                if(null != is){                    is.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }        return dest;    }

(2)把字节数组输出文件

public static void writeFileFromByteArray(byte[] src, String destPath) {        File dest = new File(destPath);        InputStream is = null;        OutputStream os = null;        try {            is = new BufferedInputStream(new ByteArrayInputStream(src));            os = new BufferedOutputStream(new FileOutputStream(dest));            byte[] buf = new byte[1024];            int len = 0;            while (-1 != (len = is.read(buf))) {                os.write(buf, 0, len);            }            os.flush();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (null != os) {                    os.close();                }                if (null != is) {                    is.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }

二.常用处理流

\ InputStream OutputStream Reader Writer 缓冲流 BufferedInputStrean BufferedOutputStream BufferedReader BufferedWriter 转换流 InputStreamReader OutputStreamWriter - - 数据流 DataInputStream DataOutputStream - - 对象流 ObjectInputStream ObjectOutStream - - 合并流 SequenceInputStream - - - 回退流 PushbackInputStream - PushbackReader - 打印流 - PrintStream - PrintWriter

1.BufferedInputStrean/BufferedOutputStream(BufferedReader/BufferedWriter类似,所以只给出一组类的例子)(BufferedReader和BufferedWrite类有新方法,一般不用多态)
这四个缓冲流增加了缓冲功能, 可以提高IO效率, 但是需要使用flush()才可以将缓冲区的内容写入实际的物理节点.
计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样可以减少访问硬盘的次数,提高传输效率。
举例:用BufferedInputStrean/BufferedOutputStream实现文件的拷贝

public static void copyFile(String srcPath, String destPath) {        File src = new File(srcPath);        File dest = new File(destPath);        BufferedReader is = null;//用子类的方法就不能用多态了        BufferedWriter os = null;        try {            is = new BufferedReader(new FileReader(src));            os = new BufferedWriter(new FileWriter(dest));            String line = null;            while(null != (line = is.readLine())){                os.write(line);                os.newLine();//换行符            }            os.flush();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            if(null != os){                try {                    os.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if(null != is){                try {                    is.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }

2.InputStreamReader/OutputStreamWriter
Java I/O流体系提供了两个转换流, 用于实现将字节流转换成字符流:

  • java.io.InputStreamReader将字节输入流转换成字符输入流
  • java.io.OutputStreamWriter将字节输出流转换成字符输出流

一般用来处理乱码问题,这里插入一个编码与解码的概念:

  • 编码:字符–编码字符集–>二进制(字节)
  • 解码:二进制(字节)–解码字符集–>字符

如果二者用的字符集不相同,那么就会出现乱码问题
举例:用InputStreamReader/OutputStreamWriter实现文件的拷贝(如果不用转换流,文件的拷贝可能会出现乱码问题,用了转换流就可以避免)

public static void main(String[] args) {        String srcPath = "D:/test01.txt";        String destPath = "D:/testcp10.txt";        File src = new File(srcPath);        File dest = new File(destPath);        BufferedReader br = null;        BufferedWriter bw = null;        try {            br = new BufferedReader(new InputStreamReader(                    new FileInputStream(src),"UTF-8"));            bw = new BufferedWriter(new OutputStreamWriter(                    new FileOutputStream(dest),"UTF-8"));            String info = null;            while(null != (info = br.readLine())){                bw.write(info);                bw.newLine();            }            bw.flush();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            if(null != bw){                try {                    bw.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if(null != br){                try {                    br.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }

3.RandomAccessFile和SequenceInputStream
(1)RandomAccessFile
java.io.RandomAccessFile与普通的Java I/O流不同的是, 他可以支持随机访问文件, 程序可以直接跳转到文件的任意地方读写数据(因此如果只是访问文件的部分内容,或向已存在的文件追加数据, RandomAccessFile是更好的选择).但RandomAccessFile也有一个局限就是只能读写文件, 不能读写其他IO节点.
RandomAccessFile对象包含了一个记录指针, 用以标识当前读写位置, 当程序新建一个RandomAccessFile对象时, 记录指针位于文件头, 当读/写n个字节后, 指针将会向后移动n个字节.除此之外RandomAccessFile还可以(前/后)自由移动该记录指针,RandomAccessFile提供了如下方法来操作文件记录指针:

方法 释义 long getFilePointer() Returns the current offset in this file. void seek(long pos) Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs.

RandomAccessFile既可以读文件, 也可以写文件, 所以他提供了完全类似于InputStream的read()方法, 也提供了完全类似于OutputStream的write()方法, 此外他还包含了一系列的readXxx() writeXxx()来完成IO(其实这几个操作是DataInput DataOutput接口来提供的, DataInputStream/DataOutputStream ObjectInputStream/ObjectOutputStream也实现了这两个接口).
在构造一个RandomAccessFile时, 需要提供一个String mode参数, 用于指定RandomAccessFile的访问模式, 该参数有如下取值:

mode 模式 “r” 只读 “rw” 读写, 如果文件不存在, 则创建 “rws” 读写,相对于”rw”,还要求对文件内容或文件元数据的更新都同步写入底层存储数据 “rwd” 读写,相对于”rw”,只要求对文件内容的更新同步写入底层存储数据

注意: RandomAccessFile不能直接向文件的指定位置插入数据,不然新插入的数据会覆盖文件的原内容.如果需要向指定的位置插入数据,程序需要先把指定插入点后的内容读入缓冲区, 等把需要插入的数据写入完成后, 再将缓冲区的内容追加到文件后面.
(2)SequenceInputStream
这是一个合并流,将多个输入流合并成一个SequenceInputStream流对象,然后再对SequenceInputStream流对象进行操作,也就是将多个InputStream合并合成一个InputStream
该对象的其中一个构造方法:SequenceInputStream(Enumeration

public class SplitFile16 {    public static void main(String[] args) throws IOException {        String destPath = "D:/test16cp.txt";        String srcPath = "D:/test01.txt";        String destPath2 = "D:/test16cp2.txt";        File src = new File(srcPath);        SplitFile16 sf = new SplitFile16(src, 6);        try {            sf.split();        } catch (IOException e) {            e.printStackTrace();        }        sf.mergeFile(destPath);        sf.mergeFile2(destPath2);    }    // 要被切割的文件(源文件)    private File src;    // 源文件的长度    private int srcLength;    // 源文件的文文件名    private String srcName;    // 要切割成的块数    private int blockNum;    // 每块子文件的长度    private int destLength;    // 切割文件指针,表示当前文件头的位置    private int current;    // 记录当前切割文件的块数    private int currentBlock;    // 最后一块的文件长度    private int finalLength;    // 子文件名    private ArrayList<String> destName;    // 分割后的文件集合,只是为了方便合并    private ArrayList<File> destFiles;    public SplitFile16(File src, int blockNum) {        if (blockNum < 1) {            throw new RuntimeException("分割块数必须大于1");        }        if (!src.exists()) {            throw new RuntimeException("传入的文件必须存在");        }        if (!src.isFile()) {            throw new RuntimeException("传入的必须是文件");        }        this.src = src;        this.blockNum = blockNum;        init();    }    // 初始化所有参数    private void init() {        srcLength = (int) src.length();        srcName = src.getName();        destLength = (int) Math.ceil(srcLength * 1.0 / blockNum);        finalLength = srcLength - destLength * (blockNum - 1);        current = 0;        currentBlock = 1;        destFiles = new ArrayList<File>();        creatDestName();    }    // 生成子文件名列表    private void creatDestName() {        destName = new ArrayList<String>();        String ParentPath = src.getParent();        for (int i = 0; i < blockNum; i++) {            destName.add(ParentPath + srcName + i);        }    }    // 分割文件    public void split() throws IOException {        while (currentBlock <= blockNum) {            File dest = new File(destName.get(currentBlock - 1));            destFiles.add(dest);    //这是为了合并文件方便,所以加的            RandomAccessFile raf = new RandomAccessFile(src, "rw");            OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));            raf.seek(current);  //设置下次读取文件时的开头位置            byte[] temp = new byte[destLength];            int length = destLength;            // 再写最后一块文件的时候,应该判断文件剩余量是否比计算出的小            if (currentBlock == blockNum) {                length = finalLength;            }            while (-1 != raf.read(temp)) {                os.write(temp, 0, length);                os.flush();                currentBlock++;                current += destLength;                break;            }            os.close();            raf.close();        }    }    // 合并文件    public void mergeFile(String destPath) throws IOException {        for (int i = 0; i < destFiles.size(); i++) {            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(destFiles.get(i)));            //FileOutputStream构造时的二个参数应改为true,可以在文件结尾追加写入            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath,true));            byte[] temp = new byte[1024];            int len = 0;            while(-1 != (len = bis.read(temp))){                bos.write(temp, 0, len);            }            bos.flush();            bos.close();            bis.close();        }    }    //合并文件,这是通过SequenceInputStream来合并    public void mergeFile2(String destPath) throws IOException {            Vector<InputStream> vc = new Vector<InputStream>();            for(int i = 0 ; i < blockNum ; i++){                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(destFiles.get(i)));                vc.add(bis);            }            //这里用SequenceInputStream来合并文件            SequenceInputStream sis = new SequenceInputStream(vc.elements());            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath,true));            byte[] temp = new byte[1024];            int len = 0;            while(-1 != (len = sis.read(temp))){                bos.write(temp, 0, len);            }            bos.flush();            bos.close();            sis.close();    }}

4.PrintStream和PrintWriter
System类中有三个属性in,out,err
(1)输入流(由键盘输入):System.in(是一个InputStream)
(2)输出流(都是PrintStream流):

  • 标准输出流(输出到控制台):System.out
  • 标准错误输出流(输出到控制台):System.err
public class PrintStreamTest14 {    public static void main(String[] args) throws FileNotFoundException {        //这是测试System.out的类型是PrintStream        System.out.println("锄禾日当午");        PrintStream ps = System.out;        ps.println("码农真辛苦");        //用PrintStream流输出到文件        ps = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File("D:/text14.txt"))));        ps.println("一本小破书");        ps.flush();        //这是测试System.in的类型是InputStream和键盘输入方法        InputStream is = System.in;        Scanner sc = new Scanner(is);        System.out.println("请输入:");        System.out.println(sc.nextLine());        //上面是键盘输入,这里我们要用文件进行输入        is = new BufferedInputStream(new FileInputStream(new File("D:/text14.txt")));        Scanner sc2 = new Scanner(is);        System.out.println(sc2.nextLine());        //把System.out重定向也就是输出重定向,让它输出到文件中(System类中有相应的方法,setIn()和setOut())        System.setOut(new PrintStream(new FileOutputStream(new File("D:/text1402.txt")),true));        System.out.println("锄禾日当午");        //把重定向后的System.out重定向回控制台,这里注意把FileDescriptor理解为控制台,java中把控制台也写成了一个类,FileDescriptor.out是一个OutputStream,而FileDescriptor的建立必须借助FileInputStream或FileOutputStream        System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));        System.out.println("一读一上午");    }}

5.DataInputStream/DataOutputStream(这两个类有新增方法一般不用多态)
基本数据类型处理流,提供了可以存取所有java基本类型数据和String类型的方法,他们允许按照java的基本数据类型读写流中的数据,读取的是java的基本数据类型(FileInputStream读取的是字节)

public class DataByteTest12 {    public static void main(String[] args) {//      String destPath = "D:/test12.txt";//      File dest = new File(destPath);//      writeTest(dest);//      readTest(dest);        //上面是测试另一组方法        String str = "这个东西第一次写的时候我纠结了1个小时";        byte[] b = write2ByteArray(str);        readFromByteArray(b);    }    //把基本数据类型写入Byte数组    public static byte[] write2ByteArray(String str){        byte[] dest = null;        ByteArrayOutputStream baos = new ByteArrayOutputStream();        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));        try {            dos.writeUTF(str);            dos.flush();            dest = baos.toByteArray();        } catch (IOException e) {            e.printStackTrace();        }finally{            if(null != dos){                try {                    dos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }         return dest;    }    //从字节数组中按照基本数据类型读取数据    public static void readFromByteArray(byte[] src){        ByteArrayInputStream bais = new ByteArrayInputStream(src);        DataInputStream dis = new DataInputStream(new BufferedInputStream(bais));        try {            System.out.println(dis.readUTF());        } catch (IOException e) {            e.printStackTrace();        }finally{            if(null != dis){                try {                    dis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    //用DataInputStream把文件中的数据按照基本数据类型读取出来    public static void readTest(File src){        DataInputStream dis = null;        try {            dis = new DataInputStream(new BufferedInputStream(new FileInputStream(src)));            System.out.println(dis.readDouble());            System.out.println(dis.readInt());            System.out.println(dis.readChar());            System.out.println(dis.readUTF());        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            if(null != dis){                try {                    dis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    //用DataOutputStream把基本数据类型写入一个文件中    public static void writeTest(File dest){        double d = 3.1415926;        int i = 233;        char c = 'a';        String s = "abcd";        DataOutputStream dos = null;        try {            dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));            dos.writeDouble(d);            dos.writeInt(i);            dos.writeChar(c);            dos.writeUTF(s);        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            try {                if(null != dos){                    dos.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

6.ObjectInputStream/ObjectOutStream(用法和DataInputStream/DataOutputStream类似)(这两个类有新增方法一般不用多态)(序列化详细介绍见《I/O篇(4)——对象序列化》)
只有序列化之后的对象才能用ObjectInputStream/ObjectOutStream流读取,不是所有的对象都可以序列化,只有实现了java.io.Serializable接口的类创建的对象才能序列化

7.PipedInputStream/PipedOutputStream(PipedReader/PipedWriter类似)
与线程有关,在后序线程相关博客中详细介绍

0 0
原创粉丝点击