IO流操作详细教程

目录

1 IO流概述和分类

2 字节流

2.1 字节流写数据

2.2 字节流写数据的3种方法

2.3 字节流写数据的两个小问题

2.4 字节流写数据加异常处理

2.5 字节流读数据(一次读一个字节数据)

2.6 字节流读数据(一次读取一个字节数组数组)

2.7 字节缓冲流(BufferedInputStream/BufferedOutputStream)

3 字符流

3.1 为什么会出现字符流

3.2 编码表

3.3 字符串编码解码

3.4 字符流中的编码解码

3.5 字符流写数据的5种方式

3.6 字符流读字符的2种方式

3.7 字符缓冲流

3.8 字符缓冲流的特有功能

4 IO流小结


1 IO流概述和分类

IO流就是用来处理设备间数据传输问题的

常见应用:文件复制,文件上传,文件下载

IO流分类

1)按照数据的流向

输入流:读数据

输出流:写数据

2)按照数据类型

字节流:字节输入流,字节输出流

字符流:字符输入流,字符输出流

一般来说,我们说IO流的分类是按照数据类型来分的

问题:这两种流在什么情况下使用呢?

如果数据可以通过windows自带的记事本软件打开且可以读懂里面的内容(没有乱码),就是用字符流,

否则使用字节流。如果不知道用那种流,就用字节流(万能流)

2 字节流

2.1 字节流写数据

字节流抽象基类

InputStream:表示字节输入流的所有类的超类

outputStream:表示字节输出流的所有类的超类 

子类名特点:子类名都是以父类为为子类后缀

使用字节输出流写数据的步骤

1)创建字节输出流对象(调用系统功能创建文件,创建字节输出流对象,让字节输出流对象指向文件)

2)调用字节输出流对象的的写数据方法

3)释放资源(关闭此文件输出流并释放与此流相关的任何系统资源)

2.2 字节流写数据的3种方法

/**
 * Created by caimh on 2019/9/11.
 */
public class FileOutPutStreamDemo {

    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("cmh.txt");
        //写数据
        //方法1
        //fos.write(97);
        //fos.write(98);
        //fos.write(99);
        //fos.write(100);
        //fos.write(101);
        //fos.write(102);
        //方法2
        final byte[] bytes = {97, 98, 99, 100, 101, 102};
        //fos.write(bytes);
        //方法3
        fos.write(bytes, 3, 2);
        //释放资源
        fos.close();
    }
}

2.3 字节流写数据的两个小问题

1)字节流写数据如何实现换行

不同系统对换行识别符是不一样的。idea编辑器自带的记事本可以识别不同系统换行符

      windows:\r\n

       linux:\n

       mac:\r

2)字节流写数据如何实现追加写入

/**
 * Created by caimh on 2019/9/11.
 */
public class FileOutPutStreamDemo {

    public static void main(String[] args) throws IOException {

        //创建字节输出流
        //FileOutputStream fos = new FileOutputStream("cmh.txt"); //非追加写入
        FileOutputStream fos = new FileOutputStream("cmh.txt", true);//追加写入

        //写数据(换行)
        for (int i = 0; i < 5; i++) {
            fos.write("hello world".getBytes());
            fos.write("\r\n".getBytes());//windows系统换行符
        }

        //释放资源
        fos.close();
    }
}

2.4 字节流写数据加异常处理

finally:在异常处理时提供finally块来执行所有清除操作。比如IO流中的释放资源

特点:被finally控制的语句一定执行,除非JVM退出

try{
    可能出现异常的代码;
}catch(异常类名 变量名){
    异常的处理代码;
}finally{
    执行所有清除操作;
}

2.5 字节流读数据(一次读一个字节数据)

1)使用字节输入流对象

2)调用字节输入流对象的读数据方法

3)释放资源

/**
 * Created by caimh on 2019/9/11.
 */
public class FileOutPutStreamDemo {

    public static void main(String[] args) throws IOException {

        //创建字节输入流
        final FileInputStream fis = new FileInputStream("cmh.txt");

        //读数据(如果到达文件的末尾,fis.read()=-1)
        int b;
        while ((b = fis.read()) != -1) {
            System.out.println(b + "-" + (char) b);
        }

        //释放资源
        fis.close();
    }
}

案例:把“F:\devTools\IDEA\HDFSClientDemo\cmh.txt”复制到模块目录下的“F:\devTools\IDEA\HDFSClientDemo\cmh_cp.txt”

思路:

1)根据数据源创建字节输入流对象

2)更具目的地创建字节输出流对象

3)读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)

4)释放资源

/**
 * Created by caimh on 2019/9/11.
 */
public class FileOutPutStreamDemo {

    public static void main(String[] args) throws IOException {

        //根据数据源创建字节输入流
        final FileInputStream fis = new FileInputStream("F:\\devTools\\IDEA\\HDFSClientDemo\\cmh.txt");
        //根据目的地创建字节输出流
        final FileOutputStream fos = new FileOutputStream("F:\\devTools\\IDEA\\HDFSClientDemo\\cmh_cp.txt");
        //读写数据(如果到达文件的末尾,fis.read()=-1)
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b);
        }
        //释放资源
        fis.close();
        fos.close();
    }
}

2.6 字节流读数据(一次读取一个字节数组数组)

/**
 * Created by caimh on 2019/9/11.
 */
public class FileOutPutStreamDemo {

    public static void main(String[] args) throws IOException {

        //创建字节输入流
        final FileInputStream fis = new FileInputStream("F:\\devTools\\IDEA\\HDFSClientDemo\\cmh.txt");
        //读取数据
        byte[] bytes = new byte[1024];//定义容器,默认数组大小1024
        int len;//读取字节的长度,-1表示没有数据
        while ((len= fis.read(bytes)) != -1) {
            //String(byte bytes[], int offset, int length),把读取的字节数组转换为字符串
            System.out.println(new String(bytes, 0, len));
        }
        //释放资源
        fis.close();
    }
}

案例:复制图片(把源“E:\tea.jpg”复制到目的地“E:\tea_cp.jpg”)

/**
 * Created by caimh on 2019/9/11.
 */
public class FileOutPutStreamDemo {

    public static void main(String[] args) throws IOException {

        //根据数据源创建字节输入流
       FileInputStream fis = new FileInputStream("E:\\tea.jpg");
        //根据目的地创建字节输出流
        FileOutputStream fos = new FileOutputStream("E:\\tea_cp.jpg");
        //读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
        byte[] bytes = new byte[1024];//定义容器,
        int len;//读取字节的长度,-1表示没有数据
        while ((len= fis.read(bytes)) != -1) {
            fos.write(bytes,0,len);
        }
        //释放资源
        fis.close();
        fos.close();
    }
}

2.7 字节缓冲流(BufferedInputStream/BufferedOutputStream)

BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。

BufferedOutputStream:通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。

构造方法:

字节缓冲输入流:BufferedInputStream(InputStream in)

字节缓冲输出流:BufferedOutputStream(OutputStream out)

为什么构造方法需要的是字节流,而不是具体的文件或者路径呢/

字节缓冲流仅仅提供缓冲区,而真正读写数据还得依靠基本的字节流对象进行操作。

字节缓冲流只是对InputStream/OutputStream的一次封装

案例:复制视频(比较四种方式性能)

把源“"E:\\movie.mp4"”复制到目的地“"E:\\movie_cp.mp4"”

/**
 * Created by caimh on 2019/9/11.
 */
public class IOStreamDemo {

    /**
     * 复制视频,四种方式实现,比较性能
     * 方式1:字节流一次读写一个字节(总共耗时:327147ms)
     * 方式2:字节流一次读写一个字节数组(总共耗时:689ms)
     * 方式3:缓冲字节流一次读写一个字节(总共耗时:2625ms)
     * 方式4:缓冲字节流一次读写一个字节数组(总共耗时:159ms)
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {

        //开始时间
        final long start = System.currentTimeMillis();

        //复制视频
        method1();
//        method2();
//        method3();
//        method4();

        //结束时间
        final long end = System.currentTimeMillis();
        System.out.println("总共耗时:" + (end - start));
    }

    /**
     * 基本字节流一次读写一个字节
     * @throws IOException
     */
    public static void method1() throws IOException {
        //创建字节流对象
        FileInputStream fis = new FileInputStream("E:\\movie.mp4");
        FileOutputStream fos = new FileOutputStream("E:\\movie_cp.mp4");

        //读写数据
        int b;
        while ((b=fis.read())!=-1)
        {
            fos.write(b);
        }

        //关闭资源
        fis.close();
        fos.close();
    }

    /**
     * 基本字节流一次读写一个字节数组
     * @throws IOException
     */
    public static void method2() throws IOException {
        //创建字节流对象
        FileInputStream fis = new FileInputStream("E:\\movie.mp4");
        FileOutputStream fos = new FileOutputStream("E:\\movie_cp.mp4");

        //读写数据(字节数组)
        byte[] bytes = new byte[1024];
        int len;
        while ((len=fis.read(bytes))!=-1)
        {
            fos.write(bytes,0,len);
        }
        //关闭资源
        fis.close();
        fos.close();
    }

    /**
     * 缓冲字节流一次读写一个字节
     * @throws IOException
     */
    public static void method3() throws IOException {
        //创建缓冲字节流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\movie.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\movie_cp.mp4"));
        //读写数据
        int b;
        while ((b=bis.read())!=-1)
        {
            bos.write(b);
        }
        //关闭资源
        bis.close();
        bos.close();
    }

    /**
     * 缓冲字节流一次读写一个字节数组
     * @throws IOException
     */
    public static void method4() throws IOException {
        //创建缓冲字节流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\movie.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\movie_cp.mp4"));
        //读写数据(字节数组)
        byte[] bytes = new byte[1024];
        int len;
        while ((len=bis.read(bytes))!=-1)
        {
            bos.write(bytes,0,len);
        }
        //关闭资源
        bis.close();
        bos.close();
    }
}

3 字符流

3.1 为什么会出现字符流

由于字节流操作中文不是特别方便,所以Java就提供字符流

字符流=字节流+编码表

用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,

如何识别是中文呢?

汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

一个汉字存储:

如果是GBK编码,占用2个字节;

如果是UTF-8编码,占用3个字节

3.2 编码表

1)计算机中存储的信息都是以二进制数表示的

2)按照某种规则,将字符存储到计算机中,称为编码;反之,将存储在计算机中的二进制数按照某种规则解析出来,称为解码。注意:按照A编码存储,必须按照A编码解析,否则会导致乱码现象。

3)字符编码:就是一套自然语言的字符与二进制数之间的对应规则(如A,65)

4)字符集:是一个系统支持的所有字符的集合,包括各国家文字,标点符号,图形符号,数字等。计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集,GBXXX字符集(常用是GBK),Unicode字符集(UTF-8,UTF-16,UTF32,常用的是UTF-8)等。

UTF-8:是电子邮件,网页及其他存储或传送文字的应用中,优先采用的编码。

UTF-8编码规则:

128个US-ASCII字符,只需要一个字节编码

拉丁文等字符,需要2个字节编码

大部分常用字(含中文),使用3个字节编码

其他极少使用的Unicode辅助字符,使用4字节编码

3.3 字符串编码解码

编码:

byte[] getBytes():使用平台默认字符集将String编码为一系列字节

byte[] getBytes(String charsetName):使用指定的字符集将String编码为一系列字节

解码:

String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String

String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String

3.4 字符流中的编码解码

字符流基类:

Reader:字符输入流的抽象类

Writer:字符输出流的抽象类

字符流中和编码解码问题相关的两个类:

InputStreamReader:是从字节流到字符流的桥梁,它读取字节,并使用指定的charset将其解码为字符。

OutputStreamWriter:是字符流到字节流的桥梁,使用指定的charset将写入的字符编码为字节。

/**
 * Created by caimh on 2019/9/11.
 */
public class IOStreamDemo {

    /**
     * 字符流操作
     * 
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {

        //创建字符输出流对象
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("cmh.txt"));//IDEA平台默认UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("cmh.txt"), "GBK");
        //读写操作
        osw.write("你好,中国!");
        osw.close();

        InputStreamReader isr = new InputStreamReader(new FileInputStream("cmh.txt"),"GBK");
        //一次读取一个字符数据
        int ch;
        while ((ch = isr.read()) != -1) {
            System.out.print((char) ch);
        }

        //释放资源
        isr.close();
    }

}

3.5 字符流写数据的5种方式

/**
 * 字符流写数据的5种方式:
 */
public class IOStreamDemo {

    public static void main(String[] args) throws IOException {

        //创建字符输出流对象(默认字符编码)
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("cmh.txt"));//IDEA平台默认UTF-8
        //写数据
        //method1(osw);
        method2(osw);
        method3(osw);
        //释放资源
        osw.close();//关闭流,先刷新流
    }

    /**
     * 写一个字符
     *      void write(int c)
     * @param osw
     * @throws IOException
     */
    public static void method1(OutputStreamWriter osw) throws IOException {
        osw.write(97);//字符流写数据,并不能直接写到文件中,最终需要通过字节流去写数据,目前还在缓冲区。
        osw.flush();//刷新流,将缓冲区数据写到文件中
        osw.write(98);
        osw.flush();
    }

    /**
     * 写一个字符数组
     *      void write(char cbuf[])
     *      void write(char cbuf[], int off, int len)
     * @param osw
     * @throws IOException
     */
    public static void method2(OutputStreamWriter osw) throws IOException {
        char[] chars = {'a', 'b', 'c', 'd', 'e', 'f'};
        //osw.write(chars);
        osw.write(chars, 0, 3);//写一个字符数组的一部分
    }

    /**
     * 写一个字符串
     *      void write(String str)
     *      void write(String str, int off, int len)
     * @param osw
     * @throws IOException
     */
    public static void method3(OutputStreamWriter osw) throws IOException {
        String str = "abcde";
        //osw.write(str);
        osw.write(str, 0, 3);//写一个字符串的一部分
    }
}

3.6 字符流读字符的2种方式

/**
 * 字符流读数据的2种方式:
 */
public class IOStreamDemo {

    public static void main(String[] args) throws IOException {

        //创建字符流对象(默认字符编码)
        InputStreamReader isr = new InputStreamReader(new FileInputStream("cmh.txt"));
        //读数据
        //method1(isr);
        method2(isr);
        //释放资源
        isr.close();
    }

    /**
     * 一次读一个字符
     * int read()
     *
     * @param isr
     * @throws IOException
     */
    public static void method1(InputStreamReader isr) throws IOException {
        int ch;
        while ((ch = isr.read()) != -1) {
            System.out.print((char) ch);
        }
    }

    /**
     * 一次读一个字符数组
     * int read(char cbuf[])
     *
     * @param isr
     * @throws IOException
     */
    public static void method2(InputStreamReader isr) throws IOException {
        char[] chs = new char[1024];
        int len;
        while ((len = isr.read(chs)) != -1) {
            System.out.print(new String(chs, 0, len));
        }
    }
}

案例:复制文件(改进版)

把源文件“F:\devTools\IDEA\HDFSClientDemo\src\main\java\com\caimh\mr\WordCountDriver.java”复制到“F:\devTools\IDEA\HDFSClientDemo\WordCountDriver_cp.java”

分析:

1)转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流提供了对应的子类

2)FileWriter:用于读取字符文件的便捷类(extends OutputStreamWriter)。FileWriter(String fileName)

3)FileReader:用于读取字符文件的便捷类(extends InputStreamWriter)。FileReader(String fileName)

4)源文件(FileReader)--->目的文件(FileWriter)

注意:如果字符流中涉及到编码解码问题,还是需要OutputStreamWriter,InputStreamWriter操作。

/**
 * 复制Java文件
 */
public class IOStreamDemo {

    public static void main(String[] args) throws IOException {

        //根据数据源创建字符输入流对象(默认字符编码)
        FileReader fr = new FileReader("F:\\devTools\\IDEA\\HDFSClientDemo\\src\\main\\java\\com\\caimh\\mr\\WordCountDriver.java");
        //根据目的地创建字符输出流对象
        FileWriter fw = new FileWriter("F:\\devTools\\IDEA\\HDFSClientDemo\\WordCountDriver_cp.java");
        //读写数据
        char[] chs = new char[1024];
        int len;
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs, 0, len);
        }
        //释放资源
        fr.close();
        fw.close();
    }

}

3.7 字符缓冲流

BufferedReader:从字符输入流读取文本,缓冲数据,以提供字符,数组和行的高效读取。可以指定缓冲区大小。

BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。可以指定缓冲区大小。

构造方法:

BufferedReader(Reader in) --参数可以是InputStreamReader,也可以是FileReader

BufferedWriter(Writer out) --参数可以是OutputStreamWriter,也可以是FileWriter

案例:复制文件(缓冲字符流改进版)

/**
 * 复制Java文件(缓冲字符流改进版)
 */
public class IOStreamDemo {

    public static void main(String[] args) throws IOException {

        //根据数据源创建字符输入流对象(默认字符编码)
        FileReader fr = new FileReader("F:\\devTools\\IDEA\\HDFSClientDemo\\src\\main\\java\\com\\caimh\\mr\\WordCountDriver.java");
        //根据目的地创建字符输出流对象
        FileWriter fw = new FileWriter("F:\\devTools\\IDEA\\HDFSClientDemo\\WordCountDriver_cp.java");
        //缓冲字符输入流
        BufferedReader br = new BufferedReader(fr);
        //缓冲字符输出流
        BufferedWriter bw = new BufferedWriter(fw);
        //读写数据
        char[] chs = new char[1024];
        int len;
        while ((len = br.read(chs)) != -1) {
            bw.write(chs, 0, len);
        }
        //释放资源
        br.close();
        bw.close();
    }
}

3.8 字符缓冲流的特有功能

BufferedWriter:

void  newLine():写一行行分隔符,行分隔符字符串由系统属性定义,可以适应不同的系统。

BufferedReader:

public String readLine():读一行文字。结果包含行的内容的字符串,不包括任何终止符,如果流的结尾已经到达,则为null.

案例:复制文件(字符缓冲流特有功能改进版)-- 优先选择这种形式

/**
 * 复制Java文件(缓冲字符流特有功能改进版)
 */
public class IOStreamDemo {

    public static void main(String[] args) throws IOException {
        //缓冲字符输入流(默认字符编码)
        BufferedReader br = new BufferedReader(new FileReader("F:\\devTools\\IDEA\\HDFSClientDemo\\src\\main\\java\\com\\caimh\\mr\\WordCountDriver.java"));
        //缓冲字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\devTools\\IDEA\\HDFSClientDemo\\WordCountDriver_cp.java"));
        //读写数据(使用字符缓冲流特有功能)
        String line;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();//换行,否则,数据写到一行了
            bw.flush();
        }
        //释放资源
        br.close();
        bw.close();
    }
}

案例:点名器

需求:一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。

案例:文件到集合

4 IO流小结

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值