Java之I/O

目录

计算机I/0体系

计算机的存储体系

通过Java操作文件 

Java操作文件类


计算机I/0体系

  • 输入设备,就是用相关的硬件把信息输入到电脑中,比如有键盘,鼠标,麦克风等,标准输入设备是键盘(将现实中的物理信号,比如光信号,电信号,波信号,变为数字,然后存储到内存中)
  • 输出设备,比如音响,打印机等,标准的输出设备是屏幕(将数字变为物理信号)
  • 内存,也称运行内存,比如我们买手机时的8+256G,其中8G就是内存,而程序中处理的信息都是要放在内存中的,它的容量较小,但是运行速度是很快的,而且它如果断电,那么数据就会消失,如果想做到关机之后数据不消失,就得看外存了
  • 外存,比如硬盘,U盘这些都是外部存储器,手机的256G就是外存的容量,外存的大小比较大,但是运行速度比较慢,断电后数据不会消失,操作系统的文件都是在硬盘中存储
  • CPU,是电脑的中央处理器,类似人的大脑,它是来控制计算机的,比如运行什么程序,和该如何运行,它是跟内存来打交道的,是从内存中去读取和输出数据的,不能直接从硬盘读取数据 
  •   

常见的I/O设备

显示器(O),触屏显示器(I/O),鼠标(I),键盘(I),摄像头(I),扬声器(O),硬盘(I/O)

计算机的存储体系

易失存储 

  • 断电后不存在
  • 跨进程管理,被抽象出的变量和对象(本质是编程语言对硬件中内存的抽象)

持久化存储

  • 断电后一般仍旧能保存
  • 通常可以跨进程读写

存储速度的差异

  • 自上而下,存储速度递减,并且读写速度越快,价格越高,所以弄出专门的存储体系
  • 内存(Memory)又被称为RAM,支持以O(1)时间复杂度,根据下标(内存地址)访问的存储

硬盘的实现

  1. 磁盘(利用磁性实现的一种存储方式)
  2. 固态硬盘(Soild State Disk SSD)
  3. 闪存(Flash Memory)
  4. 主要硬盘都是指磁盘

软件方面认识 

  • 软件方面不去考虑硬盘本身,只考虑硬盘中的数据(有数据存储的,没有数据存储的)
  • 文件是对硬盘中数据的抽象概念
  • 所以硬盘的读写问题变成文件的读写问题

OS+文件系统(FileSystem)统一管理文件

  • 文件以树形结构进行管理(不是二叉树)
  • 文件分为两种
  1. 存储数据的文件——普通文件(俗称的文件),在Window OS 下,以文件后缀(file suffiex)来标记这个文件存储的内容是什么内容(*.txt 普通文本  *.docx Word 文档)
  2. 管理树形结构组织数据的文件——目录/文件夹(directory/dir),以/结束,代表这个结点是目录

  •  这个文件树只是一个逻辑结构,而不是硬盘上的物理结构

文件的路径

  • 关于文件的路径(Path):根据一个规则,从文件树上唯一确定一个位置这个位置一定对应某个结点,但是这个结点可以不存在

路径的分类 

  1. 绝对路径(absolute path):从一棵树的根节点出发描述的路径
  2. 相对路径(relative path) :从我们所在的位置出发,所描述的路径(我们的位置一定是一个目录,不能处在一个文件上)

每个进程都有一个当前工作目录,一般一个进程的启动目录就是当前的工作目录

  • 在JAVA字符串中\不能直接写\,因为在字符串中反斜杠,表示转义的意思,所以在JAVA中\应该写成\\ 比如"E:\\JAVA代码"
  • Windows中使用\作为路径分隔符,Linux使用/作为路基分隔符,JAVA是跨平台的语言,所以我们在代码中写/或者\\都是可以的,比较推荐使用/       "E:/JAVA代码"
  • 其中.表示当前位置(目录),..表示回到当前位置的父节点(目录)
  • 根节点的父节点还是自己

文件的路径

  1. 路径是树上找到一个结点的位置
  2. 路径并不表示文件一定存在
  3. 路径有绝对路径和相对路径
  4. 当前位置:进程的当前工作目录
  5. 文件字符串中的“/”,或者“\\”表示路径分隔符
  6. .和..的含义
  7. C:/Widows/等价于C:/Windows

关于文件的操作

  1. 文件系统中,以结点为单位进行操作
  2. 文件移动操作(文件的重命名,文件的剪切+粘贴):结点移动(重命名:移动到其他节点下)
  3. 文件复制操作(复制+粘贴):新建节点+内容的复制
  4. 目录的移动操作(目录重命名 ,目录剪切+粘贴):以该节点为根的一颗子树的移动
  5. 目录复制操:以该节点为根的一颗子树的复制
  6. 删除:默认情况下只能删除普通文件或者空目录,只能删除节点,不能删除子树
  7. 删除非空目录:对整颗树的删除
  8. 关于对子树的操作,不能直接对子树进行操作,必须转换为节点操作,比如对目录的复制就变为(深度或者广度)进行遍历节点

通过Java操作文件 

文件数据=元数据+内容数据

Java操作文件类

File对象描述一个文件(结点,结点对应的文件是否存在并不一定)

构造方法

判断权限的方法 

文件的比较 

 按照文件名进行排序

  

文件的创建

  •  返回true 创建成功
  • 返回false创建失败 (文件已经存在)
  • 抛出IOException:发生IO异常(指定的目录不存在)

 

  •  临时文件的创建
  • Window下的默认路径C:\Users\用户名\AppData\Local\Temp

目录的创建

  •  mkdirszz可以创建当前路径中还不存在目录

文件的删除

  •  返回true,删除成功
  • 返回false,对应文件不存在导致的删除失败,非空目录的删除
  • IOException其他情况失败(比如其他进程打开文件)

 删除非空目录

  1. 先遍历(深度遍历或者广度遍历),删除这个目录下的所有子孙,让这个目录变成空目录
  2. 删除该目录

    private static void traversal(File dir) throws IOException {
        File []files=dir.listFiles();//遍历这个目录下的所有孩子(不是子孙)
        for (File file: files) {
            if (file.isDirectory()){
//                System.out.println(file.getCanonicalFile()+"\\");
                traversal(file);
                file.delete();
            }else {
//                System.out.println(file.getCanonicalFile());
                file.delete();
            }
        }
        dir.delete();
    }

public class Demo2 {
    public static void main(String[] args) throws IOException {
        File file=new File("E:\\JAVA代码\\IOProject\\测试目录");
        traversalDepthFirst(file);
        traversalBoardFirst(file);
    }
    //深度优先遍历 用递归
    private static void traversalDepthFirst(File dir) throws IOException {
        File[] files= dir.listFiles();
        if (files==null){
            return;
        }
        for (File file : files) {
            if (file.isDirectory()){
                System.out.println(file.getCanonicalPath()+"\\");
                traversalDepthFirst(file);
            }else {
                System.out.println(file.getCanonicalPath());
            }
        }
    }
    //广度优先遍历 用队列
    private static void traversalBoardFirst(File dir) throws IOException {
        Queue<File> queue=new LinkedList<>();
        queue.offer(dir);
        while (!queue.isEmpty()){
            File poll=queue.poll();
            if (poll.isDirectory()){
                System.out.println("[D]"+poll.getCanonicalPath());
            }else {
                System.out.println("[F]"+poll.getCanonicalPath());
            }
            if (poll.isDirectory()){
                File[] files=poll.listFiles();
                if (files==null){
                    continue;
                }
                for (File file : files) {
                    queue.offer(file);
                }
            }
        }

    }
}

判断文件类型

  •  如果不存在的话,那么对于判断是不是目录还是文件就直接返回false 

获取文件路径

       

  • 修饰过发绝对路径就是没有.或者..这些东西

  文件名的修改     

总结

  1. JDK中只提出针对结点的操作,没有提供对子树的操作,对树的操作,需要我们自己写代码完成
  2. delete()不是进入回收站的删除,而是真正的删除,回收站的删除只是把文件放到了一个固定目录
  3. 删除非空目录,先将目录的文件删完,再删空目录

处理文本数据的背后知识

数据的其后的本质是二进制数据(有范围的整数),我们可以用各种编码规则来表示文本(图片,声音的背后也是数字),比如ASCII,Unicode,UTF-8,GBK(GB18030,GB2312)

其编码规则背后原理

 各种编码的联系

乱码的原理 

文件内容的读写 —— 数据流

 读问题

  • 直接读取(以二进制数据的方式读取,表现在代码中以byte为单位
  • 文本读取

输入使用的类

  •  来自java.io.InputStream 输入流
  • 本身是一个抽象类,真正的使用要依赖于抽象类具体实现的子类

水桶就是我们的文件,输入流就是从文件输出数据到外部 

  • windows上的换行行默认是”/r/n“ ,也写作CRLF
  • 其read返回的值是-1,表示文件中文件已经读完了,-1表示EOS
  • read()就是每次只接一滴水,返回的是下个字节的数据,当返回值是-1的时候,就表示永远没水了,0表示本次没有,以后还有
  • read(byte[] b)一次接很多水,但是提取要准备好桶(byte []b),返回值是这次接到了多少滴水(范围是大于等于0,小于等于桶的大小),但是返回值是-1的时候,说明水塔中没水了

public class Demo4 {
    public static void main(String[] args) throws IOException {
        InputStream is =new FileInputStream("./hello.txt");//这个文件就是所谓的水塔
        //准备好水桶
        byte []buf=new byte[1024];
        //拿桶去接水
       int n=is.read(buf);//n表示真实接到的水多少滴(字节数量)一定小于文件中的字节数
//        System.out.println(n);
        byte []bytes= Arrays.copyOf(buf,n);
        for (byte b:bytes) {
            System.out.println(b);
        }
        is.close();//关闭水龙头
    }
}

用read()实现read(byte[] b)


public class Demo5 {
    public static void main(String[] args) throws IOException {
        InputStream is=new FileInputStream("./hello.txt");
        byte arr[]=new byte[1024];
        int i=0;
        while (true){
            int date=is.read();
            if (date==-1){
                break;
                //说明水塔中没水了
            }
            arr[i]=(byte) date;
            i++;
        }
        is.close();
    }
}

read(byte[] b)使用


public class Demo6 {
    public static void main(String[] args) throws IOException {
        InputStream is=new FileInputStream("./hello.txt");
        byte []buf=new byte[5];
        while (true){
            int n=is.read(buf);
            if (n==-1){
                break;
            }
            for (int i = 0; i < n; i++) {
                System.out.printf("%02x\n",buf[i]);
            }
        }
    }
}

Scanner进行读取

  • System.in就是一个InputStream,只是数据源来自键盘 ,终止输入用ctrl+d

写问题 

 

  • 写使用的类是OutputStream,这是一个接口,平时使用的是其子类,FileOutputStream
  • 如果不存在这个文件,则回进行创建(创建失败的可能:1权限2路径上的目录还不存在)
  • 如果存在这个文件,则会清空文件再写
  • 内存的写速度远高于硬盘的写速度,所以为了平衡这个速度差,引入了缓冲区buffer来处理

字符写入

  1. Writer是抽象类,OutputStreamWriter是实现子类(做字符编码处理)
  2. 对OutputStreamWriter再封装是PrintWriter,更加容易使用(让我们实现熟悉的print,printf,println)

几个加深理解的I/O练习 

进行普通文件的复制

package com.liusongcheng.Test;

import java.io.*;

public class CopyFile {
    public static void main(String[] args) throws IOException {
        File srcFile=new File("E:\\JAVA代码\\IOProject\\测试目录\\src.jpg");
        File destFile=new File("E:\\JAVA代码\\IOProject\\测试目录\\dest.jpg");
        byte []buf=new byte[1024];
        try (InputStream is=new FileInputStream(srcFile)){
            try (OutputStream out=new FileOutputStream(destFile)){
                while (true){
                   int read= is.read(buf);
                   if (read==-1){
                      break;
                   }
                   out.write(buf,0,read);//可能最后一次的的数据没有1024个
                   out.flush();
                }
            }
        }
    }
}

进行目录的复制

  1. 如果是目录,继续递归+目标的相对位置创建目录
  2. 如果是文件,就直接进行文件的复制
package com.liusongcheng.Test;

import java.io.*;

public class CopyDirectory {
   static final File srcFile=new File("E:\\JAVA代码\\IOProject\\测试目录");
    //将测试目录下的所有内容都复制到dest目录下,
   static final File destFile=new File("E:\\JAVA代码\\IOProject\\dest");
    public static void main(String[] args) throws IOException {
        File srcFile=new File("E:\\JAVA代码\\IOProject\\测试目录");
        //将测试目录下的所有内容都复制到dest目录下,
        File destFile=new File("E:\\JAVA代码\\IOProject\\dest");
        traversal(srcFile);
    }

    private static void traversal(File srcFile) throws IOException {
        File []files=srcFile.listFiles();
        if (files==null){
            return;
        }
        for (File file: files) {
            //怎么得到要复制位置的路径
            // 利用srcFile的绝对路径 destFile的绝对路径
            // 当前file的绝对路径 得到要复制file的绝对路径
            //利用destFile+(file-srcFile)
            String srcFilePath=srcFile.getCanonicalPath();
            String destFilePath=destFile.getCanonicalPath();
            String filePath=file.getCanonicalPath();
            String CopyFileRelativePath= filePath.substring(srcFilePath.length());
            String CopyFilePath=destFilePath+ CopyFileRelativePath;//我们要用的复制文件的绝对路径
            File oneDestFile=new File(CopyFilePath);
            if (file.isDirectory()){
                oneDestFile.mkdirs();
                traversal(file);
            }else if(file.isFile()) {
                CopyFile(file,oneDestFile);
            }
        }

    }
    private static void CopyFile(File srcFile,File destFile) throws IOException {
        byte []buf=new byte[1024];
        try (InputStream is=new FileInputStream(srcFile)){
            try (OutputStream out=new FileOutputStream(destFile)){
                while (true){
                    int read= is.read(buf);
                    if (read==-1){
                        break;
                    }
                    out.write(buf,0,read);//可能最后一次的的数据没有1024个
                    out.flush();
                }
            }
        }
    }

}

扫描指定目录,

并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件

package com.liusongcheng.Test;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class test1 {
    //扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件public static void main(String[] args) throws IOException {
       String rootPath="E:\\JAVA代码\\IOProject\\测试目录";
       String condition=".txt";//寻找包含txt的文件
        Scanner sc=new Scanner(System.in);
        List<File> ArrayList=new ArrayList<>();//存储复合条件的文件File rootFile =new File(rootPath);//创建根目录
        traversal(rootFile,condition,ArrayList);
        for (File file: ArrayList) {
            System.out.println("是否要删除这个文件"+file.getCanonicalPath());
            if (!sc.hasNextBoolean()){
                System.out.println("退出");
                return;
            }
            boolean is_delete=sc.nextBoolean();
            if (is_delete){
                file.delete();
            }
        }
    }

    private static void traversal(File rootFile, String condition, List<File> arrayList) {
        File []files=rootFile.listFiles();//得到当前文件路径下所有的子文件(不是子孙)//如果这个文件的个普通文件,不是目录,那么返回nullif (files==null){
            return;
        }
        //否则遍历这个目录下的所有文件for (File file : files) {
            if (file.isDirectory()){
                traversal(file,condition,arrayList);
            }else {
                String name=file.getName();
                if (name.contains(condition)){
                    arrayList.add(file);
                }
            }
        }
    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

库里不会投三分

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值