IO技术二(常用流)

目录

文件字节流

将字符串/字节数组的内容写入到文件中

利用文件流实现文件的复制

文件字符流

使用 FileReader 与 FileWriter 实现文本文件的复制

缓冲字节流

使用缓冲流实现文件的高效率复制

缓冲字符流

使用 BufferedReader 与 BufferedWriter 实现文本文件的复制

字节数组流

简单测试 ByteArrayInputStream 的使用

数据流

DataInputStream 和 DataOutputStream 的使用

对象流

ObjectInputStream/ObjectOutputStream 的使用

转换流

使用 InputStreamReader 接收用户的输入,并输出到控制台

随意访问文件流

RandomAccessFile 的使用


文件字节流

        FileInputStream 通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文 本文件等)。Java 也提供了 FileReader 专门读取文本文件。

        FileOutputStream 通过字节的方式写数据到文件中,适合所有类型的文件。Java 也提 供了 FileWriter 专门写入文本文件。

将字符串/字节数组的内容写入到文件中

import java.io.FileOutputStream;
import java.io.IOException;

public class TestFileOutputStream {
    public static void main(String[ ] args) {
        FileOutputStream fos = null;
        String string = "欢迎学习Java!";
        try {
            // true 表示内容会追加到文件末尾;false 表示重写整个文件内容。
            fos = new FileOutputStream("d:/a.txt", true);
            //该方法是直接将一个字节数组写入文件中; 而 write(int n)是写入一个字节
            fos.write(string.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

        用到一个 write 方法:void write(byte[ ] b),该方法不再一个字节一个字节地写入,而是直接写入一个字节数组;另外其还有一个重载的方法:void write(byte[ ] b, int off, int length),这个方法也是写入一个字节数组,但是我们程序员可以指定从字节数组 的哪个位置开始写入,写入的长度是多少。

利用文件流实现文件的复制

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestFileCopy {

    public static void main(String[ ] args) {
        //将 a.txt 内容拷贝到 b.txt
        copyFile("d:/a.txt", "d:/b.txt");
    }
    
    /**
     * 将 src 文件的内容拷贝到 dec 文件
     * @param src 源文件
     * @param dec 目标文件
     */
    static void copyFile(String src, String dec) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        //为了提高效率,设置缓存数组!(读取的字节数据会暂存放到该字节数组中)
        byte[ ] buffer = new byte[1024];
        int temp = 0;
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dec);
            //边读边写
            //temp 指的是本次读取的真实长度,temp 等于-1 时表示读取结束
            while ((temp = fis.read(buffer)) != -1) {
                /*将缓存数组中的数据写入文件中,注意:写入的是读取的真实长度;
                 *如果使用 fos.write(buffer)方法,那么写入的长度将会是 1024,即缓存
                 *数组的长度*/
                fos.write(buffer, 0, temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //两个流需要分别关闭
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在使用文件字节流时,我们需要注意以下两点:

  1. 为了减少对硬盘的读写次数,ᨀ高效率,通常设置缓存数组。相应地,读取时使用的方 法为:read(byte[ ] b);写入时的方法为:write(byte[ ] b, int off, int length)。
  2. 程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流 无法关闭的情况。

文件字符流

        文件字节流可以处理所有的文件,但是字节流不能很好的处理 Unicode 字 符,经常会出现“乱码”现象。所以,我们处理文本文件,一般可以使用文件字符流,它以字符为单位进行操作。

使用 FileReader 与 FileWriter 实现文本文件的复制

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class TestFileCopy2 {
    public static void main(String[ ] args) {
        // 写法和使用 Stream 基本一样。只不过,读取时是读取的字符。
        FileReader fr = null;
        FileWriter fw = null;
        int len = 0;
        try {
            fr = new FileReader("d:/a.txt");
            fw = new FileWriter("d:/d.txt");
            //为了提高效率,创建缓冲用的字符数组
            char[ ] buffer = new char[1024];
            //边读边写
            while ((len = fr.read(buffer)) != -1) {
                fw.write(buffer, 0, len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

缓冲字节流

        Java 缓冲流本身并不具有 IO 流的读取与写入功能,只是在别的流(节点流或其他处理 流)上加上缓冲功能ᨀ高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流(包 装流)。

        当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这时如果使用缓冲流就 能够更高效的读写信息。因为缓冲流是先将数据缓存起来,然后当缓存区存满后或者手动刷 新时再一次性的读取到程序或写入目的地。

        因此,缓冲流还是很重要的,我们在 IO 操作时记得加上缓冲流来ᨀ升性能。

        BufferedInputStream 和 BufferedOutputStream 这两个流是缓冲字节流,通过内部缓存数 组来提高操作流的效率。

使用缓冲流实现文件的高效率复制

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestBufferedFileCopy1 {

    public static void main(String[ ] args) {
        // 使用缓冲字节流实现复制
        long time1 = System.currentTimeMillis();
        copyFile1("D:/电影/你的电影.mkv", "D:/电影/你的电影1.mkv");
        long time2 = System.currentTimeMillis();
        System.out.println("缓冲字节流花费的时间为:" + (time2 - time1));
        // 使用普通字节流实现复制
        long time3 = System.currentTimeMillis();
        copyFile2("D:/电影/你的电影.mkv", "D:/电影/你的电影2.mkv");
        long time4 = System.currentTimeMillis();
        System.out.println("普通字节流花费的时间为:" + (time4 - time3));
    }

    /**
     * 缓冲字节流实现的文件复制的方法
     * @param src
     * @param dec
     */
    static void copyFile1(String src, String dec) {
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        int temp = 0;
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dec);
            //使用缓冲字节流包装文件字节流,增加缓冲功能,提高效率
            //缓存区的大小(缓存数组的长度)默认是 8192,也可以自己指定大小
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            while ((temp = bis.read()) != -1) {
                bos.write(temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //注意:增加处理流后,注意流的关闭顺序!“后开的先关闭!”
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null) {
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 普通节流实现的文件复制的方法
     * @param src
     * @param dec
     */
    static void copyFile2(String src, String dec) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        int temp = 0;
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dec);
            while ((temp = fis.read()) != -1) {
                fos.write(temp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

copyFile2 好几分钟都没复制完,我强制停止了

在关闭流时,应该先关闭最外层的包装流,即“后开的先关闭”。

缓存区的大小默认是 8192 字节,也可以使用其它的构造方法自己指定大小。

缓冲字符流

        BufferedReader/BufferedWriter 增加了缓存机制,大大ᨀ高了读写文本文件的效率,同时, 提供了更方便的按行读取的方法:readLine(); 处理文本时,我们一般可以使用缓冲字符流。

使用 BufferedReader 与 BufferedWriter 实现文本文件的复制

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class TestBufferedFileCopy2 {
    public static void main(String[] args) {
        // 注:处理文本文件时,实际开发中可以用如下写法,简单高效!!
        FileReader fr = null;
        FileWriter fw = null;
        BufferedReader br = null;
        BufferedWriter bw = null;
        String tempString = "";
        try {
            fr = new FileReader("d:/a.txt");
            fw = new FileWriter("d:/d.txt");
            //使用缓冲字符流进行包装
            br = new BufferedReader(fr);
            bw = new BufferedWriter(fw);
            //BufferedReader 提供了更方便的 readLine()方法,直接按行读取文本
            //br.readLine()方法的返回值是一个字符串对象,即文本中的一行内容
            while ((tempString = br.readLine()) != null) {
                //将读取的一行字符串写入文件中
                bw.write(tempString);
                //下次写入之前先换行,否则会在上一行后边继续追加,而不是另起一行
                bw.newLine();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

readLine()方法是 BufferedReader 特有的方法,可以对文本文件进行更加方便的读取操作。

写入一行后要记得使用 newLine()方法换行。

字节数组流

        ByteArrayInputStream 和 ByteArrayOutputStream 经常用在需要流和数组之间转化的情 况!

        说白了,FileInputStream 是把文件当做数据源。ByteArrayInputStream 则是把内存中的” 某个字节数组对象”当做数据源。

简单测试 ByteArrayInputStream 的使用

import java.io.ByteArrayInputStream;
import java.io.IOException;

public class TestByteArray {

    public static void main(String[ ] args) {
        //将字符串转变成字节数组
        byte[ ] b = "abcdefg".getBytes();
        test(b);
    }

    public static void test(byte[ ] b) {
        ByteArrayInputStream bais = null;
        StringBuilder sb = new StringBuilder();
        int temp = 0;
        //用于保存读取的字节数
        int num = 0;
        try {
            //该构造方法的参数是一个字节数组,这个字节数组就是数据源
            bais = new ByteArrayInputStream(b);
            while ((temp = bais.read()) != -1) {
                sb.append((char) temp);
                num++;
            }
            System.out.println(sb);
            System.out.println("读取的字节数:" + num);
        } finally {
            try {
                if (bais != null) {
                    bais.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

数据流

        数据流将“基本数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式 从底层输入输出流中操作 Java 基本数据类型与字符串类型。

        DataInputStream 和 DataOutputStream ᨀ供了可以存取与机器无关的所有 Java 基础类型 数据(如:int、double、String 等)的方法。

        DataInputStream 和 DataOutputStream 是处理流,可以对其他节点流或处理流进行包装, 增加一些更灵活、更高效的功能。

DataInputStream 和 DataOutputStream 的使用

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestDataStream {
    public static void main(String[] args) {
        DataOutputStream dos = null;
        DataInputStream dis = null;
        FileOutputStream fos = null;
        FileInputStream fis = null;
        try {
            fos = new FileOutputStream("D:/data.txt");
            fis = new FileInputStream("D:/data.txt");
            //使用数据流对缓冲流进行包装,新增缓冲功能
            dos = new DataOutputStream(new BufferedOutputStream(fos));
            dis = new DataInputStream(new BufferedInputStream(fis));
            //将如下数据写入到文件中
            dos.writeChar('a');
            dos.writeInt(10);
            dos.writeDouble(Math.random());
            dos.writeBoolean(true);
            dos.writeUTF("心目中的Java大学");
            //手动刷新缓冲区:将流中数据写入到文件中
            dos.flush();
            //直接读取数据:读取的顺序要与写入的顺序一致,否则不能正确读取数据。
            System.out.println("char: " + dis.readChar());
            System.out.println("int: " + dis.readInt());
            System.out.println("double: " + dis.readDouble());
            System.out.println("boolean: " + dis.readBoolean());
            System.out.println("String: " + dis.readUTF());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(dos!=null){
                    dos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(dis!=null){
                    dis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fos!=null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fis!=null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

使用数据流时,读取的顺序一定要与写入的顺序一致,否则不能正确读取数据。

对象流

        数据流只能实现对基本数据类型和字符串类型的读写,并不能读取对象 (字符串除外),如果要对某个对象进行读写操作,我们需要学习一对新的处理流: ObjectInputStream/ObjectOutputStream。

        ObjectInputStream/ObjectOutputStream 是以“对象”为数据源,但是必须将传输的对象进 行序列化与反序列化操作。

ObjectInputStream/ObjectOutputStream 的使用

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Date;

public class TestObjectStream {
    public static void main(String[] args) {
        write();
        read();
    }
    /**
     * 使用对象输出流将数据写入文件
     */
    public static void write(){
        // 创建 Object 输出流,并包装缓冲流,增加缓冲功能
        OutputStream os = null;
        BufferedOutputStream bos = null;
        ObjectOutputStream oos = null;
        try {
            os = new FileOutputStream(new File("d:/bjsxt.txt"));
            bos = new BufferedOutputStream(os);
            oos = new ObjectOutputStream(bos);
            // 使用 Object 输出流
            //对象流也可以对基本数据类型进行读写操作
            oos.writeInt(12);
            oos.writeDouble(3.14);
            oos.writeChar('A');
            oos.writeBoolean(true);
            oos.writeUTF("哪都通大学");
            //对象流能够对对象数据类型进行读写操作
            //Date 是系统ᨀ供的类,已经实现了序列化接口
            //如果是自定义类,则需要自己实现序列化接口
            oos.writeObject(new Date());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭输出流
            if(oos != null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os != null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用对象输入流将数据读入程序
     */
    public static void read() {
        // 创建 Object 输入流
        InputStream is = null;
        BufferedInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            is = new FileInputStream(new File("d:/bjsxt.txt"));
            bis = new BufferedInputStream(is);
            ois = new ObjectInputStream(bis);
            // 使用 Object 输入流按照写入顺序读取
            System.out.println(ois.readInt());
            System.out.println(ois.readDouble());
            System.out.println(ois.readChar());
            System.out.println(ois.readBoolean());
            System.out.println(ois.readUTF());
            System.out.println(ois.readObject().toString());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭 Object 输入流
            if(ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

转换流

        InputStreamReader/OutputStreamWriter 用来实现将字节流转化成字符流。比如,如下场 景:

        System.in 是字节流对象,代表键盘的输入,如果我们想按行接收用户的输入时,就必 须用到缓冲字符流 BufferedReader 特有的方法 readLine(),但是经过观察会发现在创建 BufferedReader 的 构 造 方 法 的 参 数 必 须 是 一 个 Reader 对 象 , 这 时 候 我 们 的 转 换 流 InputStreamReader 就派上用场了。

        而 System.out 也是字节流对象,代表输出到显示器,按行读取用户的输入后,并且要将 读取的一行字符串直接显示到控制台,就需要用到字符流的 write(String str)方法,所以我们 要使用 OutputStreamWriter 将字节流转化为字符流。

使用 InputStreamReader 接收用户的输入,并输出到控制台

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class TestConvertStream {
    public static void main(String[] args) {
        // 创建字符输入和输出流:使用转换流将字节流转换成字符流
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = new BufferedReader(new InputStreamReader(System.in));
            bw = new BufferedWriter(new OutputStreamWriter(System.out));
            // 使用字符输入和输出流
            String str = br.readLine();
            // 一直读取,直到用户输入了 exit 为止
            while (!"exit".equals(str)) {
                // 写到控制台
                bw.write(str);
                bw.newLine();// 写一行后换行
                bw.flush();// 手动刷新
                // 再读一行
                str = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭字符输入和输出流
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

随意访问文件流

RandomAccessFile 可以实现两个作用:

  1. 实现对一个文件做读和写的操作。
  2. 可以访问文件的任意位置。不像其他流只能按照先后顺序读取。

        在开发某些客户端软件时,经常用到这个功能强大可以”任意操作文件内容”的类。比如, 软件的使用次数和使用日期,可以通过本类访问文件中保存次数和日期的地方进行比对和修 改。 Java 很少开发客户端软件,所以在 Java 开发中这个类用的相对较少。

学习这个流我们需掌握三个核心方法:

  1. RandomAccessFile(String name, String mode) name 用来确定文件; mode 取 r(读)或 rw(可读写),通过 mode 可以确定流对文件的访问权限。
  2. seek(long a) 用来定位流对象读写文件的位置,a 确定读写位置距离文件开头 的字节个数。
  3. getFilePointer() 获得流的当前读写位置。

RandomAccessFile 的使用

import java.io.IOException;
import java.io.RandomAccessFile;

public class TestRandomStream {
    public static void main(String[] args) {
        RandomAccessFile raf = null;
        try {
            // 将若干数据写入到 a.txt 文件
            int[ ] data = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
            raf = new RandomAccessFile("d:/a.txt", "rw");
            for (int i = 0; i < data.length; i++) {
                raf.writeInt(data[i]);
            }
            // 直接从 a.txt 中读取数据,位置为从 36 字节开始。
            raf.seek(4);
            System.out.println(raf.readInt()); // 读取 4 个字节(int 为 4 个字节)
            // 直接从 a.txt 中读取数据,隔一个读一个数据
            for (int i = 0; i < 10; i += 2) {
                raf.seek(i * 4);
                System.out.print(raf.readInt() + "\t");
            }
            System.out.println(); // 换行
            // 在 8 字节处插入一个新数据 45,替换以前的数据 30
            raf.seek(8);
            raf.writeInt(45);
            for (int i = 0; i < 10; i++) {
                raf.seek(i * 4);
                System.out.print(raf.readInt() + "\t");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值