java IO流

IO流特点

IO流属于物理连接,需要手动显示close(),否则内存泄漏
File对象必须是文件路径而不能是文件目录,否则在使用流时会报错java.io.FileNotFoundException: D:\io\io1\io2 (拒绝访问。)

IO流的分类

  • 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流

为什么这样分类?

1,数据单位

  • 字节流(8bit = 255位):适合图像视频等(.jpg .mp3 .mp4 .avi .doc .ppt…)传输,因为电脑像素点的灰度值范围是[0,255]
  • 字符流(16bit = 2字节):适合传输文本(.java .c .cpp .txt),Java语言规范规定,Java的char类型是UTF-16的code unit,也就是一定是16位(2字节)

2,流向

输出流: 程序(内存)——>存储设备

输入流:键盘,磁盘等——>程序(内存)

3,角色

节点流:直接读写数据,最简单的一类流

处理流:以节点流对象为形参,为节点流提供更好的性能的流
在这里插入图片描述

  • 基类(抽象类)给定了节点流的规范
  • 子类的命名后缀规则一致,虽然有40多个类,但都很规范
    在这里插入图片描述

字符流:Reader和Writer(以FileReader和FileWriter为例)

(1)FileWriter

  • 主要方法
方法解释
构造器
FileWriter(File路径or对象,可选项append)
append默认为false
true表示write方法追加写入
false表示write方法覆盖写入
write(“写入的内容”)
write(String或char[],offset,len)
可以传入String或char[ ]
offset表示从索引offset号开始传入
len表示传入的长度,注意角标越界问题
flush(空参)刷新,不刷新的话,虽然写入了,但是read不到,文件打开也看不到写入
close()显示关闭流,防止内存溢出
  • 示例
    准备工作:先创建好file

    File file = new File("text.txt");//在当前project下创建一个text.txt文件
    if(!file.exists()){
        file.createNewFile();//防止FileNotFoundException: text.txt (系统找不到指定的文件。)
    }
    
      /***************Writer操作*******************/
          FileWriter fileWriter = new FileWriter(file,true);//流的实例化
          //append为true则多个write是追加写入
          //append为false则write是覆盖写入
    
          fileWriter.write("hello world\n");
          fileWriter.flush();//刷新,不刷新的话,虽然写入了,但是read不到,文件打开也看不到写入
          fileWriter.write("it's a test\n");
          fileWriter.flush();//刷新
      //该程序运行几遍就write几遍,下面read就会重复几遍
      	fileWriter.close();
    

(2)FileReader

  • 主要方法
方法解释
构造器
FileReader(File对象或路径)
FileReader(FileDescriptor fd)
/
read(空参)空参迭代返回该文件的一个字符的ASCII码值data,需配合强转char输出
read(char[] cbuf)迭代返回本次流读取的长度len,并把读取的数去填入cbuf数组中
read(char[] cbuf,偏移量offset,最大读取长度length)length为0时线程阻塞,length为装填长度
read方法返回值
若某段流没有可读字符,read返回-1
  当FileReader流能read到字符时,返回值len=实际读取长度>0
  读取不到返回-1,所以可以利用len!=-1创建循环条件
  注意区别read返回值 和 对cbuf赋值的差别

read()是一个迭代器,无论是read(空参)还是read(cbuf)都会导致迭代器指针移动

  • 解释:什么叫“目的缓冲区”和“装填长度”?为什么read方法最好不要带offset和len参数?

1.char[] cbuf数组就是目的缓冲区,把cbuf传入read()的形参,则会把目标文件中cbuf.length()个连续的字符读入cbuf,然后遍历cbuf数组,故称“缓冲”目标缓冲区(cbuf数组)设置过大则内存占用大,过小则运行速度慢。
2.而read(cbuf,offset,len)中的len就是装填长度,装填长度不影响最后read输出结果,但可能造成角标越界
3.len为0线程阻塞,offset会影响读取结果,所以通常不带这两个参数

  • 示例

      	/*************Reder操作**************/
          FileReader fileReader = new FileReader(file);//流的实例化
    
          char[] cbuf = new char[6];//设置“目的缓冲区”
          int len ; //定义“装填长度”
    
          while((len=fileReader.read(cbuf,0,6)) != -1){//最好是不带后面两个形参
          //当FileReader流能read到字符时,返回值len=实际读取长度>0
      //形参解析:目的缓冲区数组,offset索引偏移量(为0时保证从cbuf[0]开始装填),
      // len最大装填长度:len+offset之和不能超过cbuf.length()否则报错指针越界
            //输出的方式1:注意len和length区别
              for(int i = 0 ; i<len ; i++){
                  //必须是i<len“装填长度”,而不能是i<cbuf.length“缓冲区长度”
                  System.out.print(cbuf[i]);
                  }
                  }
           fileReader.close();
    

输出结果

开发规范

为了防止各种报错,开发时最好满足以下规范

  • close()防止内存溢出
  • try-catch-finally而不是简单的throws IOException
  • close()放在finally中,以便出问题时及时关闭流
  • 在finally中的close()还需要if判断是否存在,并嵌套try-catch
  • 使用IDEA的alt+insert调try-catch时,会自动在外边生成FileWriter fw = null;和FileReader fr = null;

字节流:InputStream和OutputStream

在这里插入图片描述

  • 步骤规范:创建File对象——创建流对象——操作——关闭流

我们把刚刚创建的“C:\Users\Administrator\IdeaProjects\ZJHindi\text.txt”复制到“D:\io\io1\io2”下

  1. 关掉QQ(热键冲突)Alt+Ctrl+T调用try-catch-finally
    在这里插入图片描述
  2. 第二步
    在这里插入图片描述
  3. 第三步,两个close()分别try -catch(注意去掉throws IOException)

在这里插入图片描述
完整代码:

package zjh;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyTest {
    public static void main(String[] args){

        FileReader fr = null;//从源文件读取
        FileWriter fw = null;//写入目的文件
        try {
            File scrfile = new File("C:\\Users\\Administrator\\IdeaProjects\\ZJHindi\\text.txt");
            File destfile = new File("D:\\io\\io1\\io2\\desttext.txt");


            fr = new FileReader(scrfile);
            fw = new FileWriter(destfile);

            char[] cbuf = new char[5];
            int len;

            while((len=fr.read(cbuf))!=-1){
                fw.write(cbuf);
            }

            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            if(fw!=null){//先判断fw非空
            try {
                fw.close();//防止关闭时出错
            } catch (IOException e) {
                e.printStackTrace();
            }
            }

            if(fr!=null) {//判断fr非空
                try {
                    fr.close();//关闭
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        }
}

处理流:缓冲流:BufferedInputStream和BufferedOutputStream

  • 把节点流作为形参new缓冲流,套接在已有的节点流之外
  • 传统的节点流是线程阻塞的,而缓冲流则通过在内存中开辟一块缓冲区用于各节点流缓冲,缓冲满之后执行,利用空间换时间
  • 外层处理流close()的时候,内层字节流也会关闭,因此只需对外层流finally{close()}

如下是一个封装好的图片复制方法:

    public static void BufferedStreamCopy(String srcPath,String destPath) {//缓冲流字节流的复制方法
        //形参直接放入源文件路径 和 目的文件路径,文件自动复制
        BufferedInputStream bis = null;//写的时候按正常步骤写
        BufferedOutputStream bos = null;//这两句是try-catch-finally自动生成的

        try {
//File对象的创建,导入路径
            File srcfile = new File(srcPath);
            File destfile = new File(destPath);

//节点流的创建,用于复制图像视频等
            FileInputStream fis = new FileInputStream(srcfile);
            FileOutputStream fos = new FileOutputStream(destfile);

//处理流(缓冲流)的创建,用于加速
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //原本对节点流的操作照搬给处理流
            byte[] bbuf = new byte[1024];
            int len;

            while((len=bis.read(bbuf)) != -1){
                //每从bis读一次,就往bos写一次
                bos.write(bbuf);
                }
            //因为有处理流自动flush,所以不用显示的flush
            //缓冲区满了之后自动会flush,默认长度1024x8
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
//关闭流,只需要关闭外层
            if(bis!=null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bos!=null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            }
        }

调用一下:

public static void main(String[] args) {

BufferedStreamCopy("D:\\io\\io1\\io2\\QQ截图20220115002227.png"
,"D:\\io\\io2\\io3\\新建文件夹\\复制后的图.png");//目的路径也必须是文件,并且格式(.png)最好相同

}

字符流处理文本——>字节流处理图片视频

  • 把FileReader——>FileInputStream,把FileWriter——>FileOutputStream
  • 把char[] cbuf——>byte[] bbuf

其实字节流也可以复制(仅仅是复制)文本文件,只是中途不能控制台输出,否则中文会被切分

应用

加密算法:适用于所有文件,加密解密方法相同(^5)

public static void MethodEncrpt(String srcpath,String destpath){//定义方法:加密,解密
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        bis = new BufferedInputStream(new FileInputStream(new File(srcpath)));
        bos = new BufferedOutputStream(new FileOutputStream(new File(destpath)));

        int data;
        while((data= bis.read()) != -1){
            bos.write(data^5);//异或加密,加密解密方式都相同(M^n^n=M)
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {

        if (bos != null) {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bis != null) {
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  /********************加密解密方法使用示例********************/
public static void main(String[] args) {
//加密
MethodEncrpt("C:\\Users\\Administrator\\Desktop\\数据结构文档\\00_课程介绍.pdf","D:\\io\\加密\\字节流文件\\加密数据结构课程介绍.pdf");

//解码
        MethodEncrpt("D:\\io\\加密\\字节流文件\\加密数据结构课程介绍.pdf","D:\\io\\解密\\字节流文件\\解密1.pdf");


MethodEncrpt("D:\\io\\io1\\io2\\desttext.txt","D:\\io\\io1\\io3\\加密1.txt");
MethodEncrpt("D:\\io\\io1\\io3\\加密1.txt","D:\\io\\io1\\io3\\解密1.txt");
    }

加密后

字数统计算法:加入了转换流

注意:实际开发中应该用try-catch-finally,此处为了方便直接throws IOException

public static void MethodCount(String path) throws IOException {

    File file = new File(path);
    FileInputStream fis = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(fis);
    InputStreamReader isr = new InputStreamReader(bis);
    HashMap<Character,Integer> hsm1 = new HashMap<>();

    int len;
    char[] cbuf = new char[1024];
    while((len=isr.read(cbuf)) != -1){//读取到cbuf
        for(int i=0 ; i<len ; i++){
            if(hsm1.containsKey(cbuf[i])){
                hsm1.replace(cbuf[i],hsm1.get(cbuf[i])+1);
            }
            else{
                hsm1.put(cbuf[i],1);
            }
        }
    }




    //输出遍历hsm1数组
    Set<Map.Entry<Character, Integer>> entries = hsm1.entrySet();
    Iterator<Map.Entry<Character, Integer>> iterator = entries.iterator();
    while (iterator.hasNext()){
        Map.Entry<Character, Integer> next = iterator.next();

        Character key = next.getKey();
        Integer value = next.getValue();

        switch (key) {
            case ' ':
                System.out.println("空格:"+value+"次");
                break;
            case '\t':
                System.out.println("tab:"+value+"次");
                break;
            case '\r':
                System.out.println("回车:"+value+"次");
                break;
            case '\n':
                System.out.println("换行:"+value+"次");
                break;
            default:
                System.out.println(key+":"+value+"次");
                break;
        }
    }
    }

在这里插入图片描述

加密解密GUI

在这里插入图片描述在这里插入图片描述

public class EncrptDir {
public static void EncrptMethod(String dirpath) throws IOException {//文件目录
    File srcfile = new File(dirpath);
    File destfile = new File("D:\\加密照片\\加密照片5");//加密照片存储地址
    if(!destfile.exists()){
        destfile.mkdirs();
    }
    File[] files = srcfile.listFiles();//罗列所有照片的File对象

    for(int i = 0 ; i<files.length ; i++){//处理每个文件
        String s = files[i].getName().split("\\.")[1];//s表示文件名后缀
        //split方法是正则表达式,所以要用\\.
        String d = "加密图片"+i+"." + s;
        File file_jm = new File(destfile,d);
        //创建节点流
        FileInputStream fis = new FileInputStream(files[i]);//输入原图
        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream(file_jm);//输出加密
        BufferedOutputStream bos = new BufferedOutputStream(fos);
    //读写操作
       int data;
        while((data=bis.read())!=-1){
            bos.write(data^5);//加密操作
        }

    //关闭流
        bos.close();
        bis.close();
        System.out.println("正在加密第  "+i+"  张图");
    }
    System.out.println("加密完成,文件位于D:\\加密照片\\加密照片5");
    }

public static void DecrptMethod(String dirpath) throws IOException {//文件目录

    File srcfile = new File(dirpath);
    File destfile = new File("D:\\解密照片\\解密照片5");//解密照片存储地址
    if(!destfile.exists()){
        destfile.mkdirs();
    }
    File[] files = srcfile.listFiles();//罗列所有照片的File对象

    for(int i = 0 ; i<files.length ; i++){//处理每个文件
        String s = files[i].getName().split("\\.")[1];//s表示文件名后缀
        //split方法是正则表达式,所以要用\\.
        String d = "加密图片"+i+"." + s;
        File file_jm = new File(destfile,d);
        //创建节点流
        FileInputStream fis = new FileInputStream(files[i]);//输入原图
        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream(file_jm);//输出解密
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //读写操作
        int data;
        while((data=bis.read())!=-1){
            bos.write(data^5);//解密操作
        }

        //关闭流
        bos.close();
        bis.close();

        System.out.println("正在解密第  "+i+"  张图");
    }
    System.out.println("解密完成,文件位于D:\\解密照片\\解密照片5");
    }}



package ZJHindi;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;

public class EncrptGUI {
    public static void main(String[] args) {
        JFrame jf = new JFrame("文件加密工具");
        jf.setLayout(new FlowLayout(FlowLayout.LEFT));
        jf.setBounds(460,300,350,200);

        /*********逻辑部分**********/

        JLabel label1 = new JLabel("需要加密or解密的文件源路径:");
        JTextField srcfilepath = new JTextField("",30);

        JButton jButton1 = new JButton("点击加密");
        jButton1.setSize(20,30);

        JLabel jLabe2 = new JLabel("输出位置D:\\加密照片\\加密照片5");

        jf.add(label1);
        jf.add(srcfilepath);
        jf.add(jButton1);
        jf.add(jLabe2);
        JButton jButton2 = new JButton("点击解密");
        jf.add(jButton2);
        JLabel jLabe3 = new JLabel("输出位置D:\\解密照片\\解密照片5");
        jf.add(jLabe3);
        /****************************/
        jf.setVisible(true);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JLabel outter = new JLabel("执行信息:");
        jf.add(outter);
        JTextArea jta = new JTextArea();
        jf.add(jta);

/****************************加密*********************/
        jButton1.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String path = srcfilepath.getText();//获取输入的路径
                try {
                    EncrptDir.EncrptMethod(path);
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
jta.append("加密完成\n");

            }
            });
/******************解密*****************/
        jButton2.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String path = srcfilepath.getText();//获取输入的路径
                try {
                    EncrptDir.DecrptMethod(path);
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                jta.append("解密完成\n");
            }
        });
    }
}

转换流:字节——>字符

转换流只有继承于Reader和Writer的,即只能从字节转到字符
在这里插入图片描述

InputStreamReader写入流转换:解码

因为读取的文件有一个确定的编码集,所以解码时必须按该编码集,否则会乱码

  • 构造函数默认为UTF-8
  • 自定义构造new InputStreamReader(内层字节流 , "GBK"或"UTF-8"等等);

OutputStreamWriter导出流转换:编码

编码即导出到硬盘中,写入到文件中,所以用什么编码都一样,只需要保证后续解码的时候对应的编码集相同。
“GBK”“UTF-8”底层都是用unicode存储的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值