目录
计算机I/0体系
- 输入设备,就是用相关的硬件把信息输入到电脑中,比如有键盘,鼠标,麦克风等,标准输入设备是键盘(将现实中的物理信号,比如光信号,电信号,波信号,变为数字,然后存储到内存中)
- 输出设备,比如音响,打印机等,标准的输出设备是屏幕(将数字变为物理信号)
- 内存,也称运行内存,比如我们买手机时的8+256G,其中8G就是内存,而程序中处理的信息都是要放在内存中的,它的容量较小,但是运行速度是很快的,而且它如果断电,那么数据就会消失,如果想做到关机之后数据不消失,就得看外存了
- 外存,比如硬盘,U盘这些都是外部存储器,手机的256G就是外存的容量,外存的大小比较大,但是运行速度比较慢,断电后数据不会消失,操作系统的文件都是在硬盘中存储
- CPU,是电脑的中央处理器,类似人的大脑,它是来控制计算机的,比如运行什么程序,和该如何运行,它是跟内存来打交道的,是从内存中去读取和输出数据的,不能直接从硬盘读取数据
常见的I/O设备
显示器(O),触屏显示器(I/O),鼠标(I),键盘(I),摄像头(I),扬声器(O),硬盘(I/O)
计算机的存储体系
易失存储
- 断电后不存在
- 跨进程管理,被抽象出的变量和对象(本质是编程语言对硬件中内存的抽象)
持久化存储
- 断电后一般仍旧能保存
- 通常可以跨进程读写
存储速度的差异
- 自上而下,存储速度递减,并且读写速度越快,价格越高,所以弄出专门的存储体系
- 内存(Memory)又被称为RAM,支持以O(1)时间复杂度,根据下标(内存地址)访问的存储
硬盘的实现
- 磁盘(利用磁性实现的一种存储方式)
- 固态硬盘(Soild State Disk SSD)
- 闪存(Flash Memory)
- 主要硬盘都是指磁盘
软件方面认识
- 软件方面不去考虑硬盘本身,只考虑硬盘中的数据(有数据存储的,没有数据存储的)
- 文件是对硬盘中数据的抽象概念
- 所以硬盘的读写问题变成文件的读写问题
OS+文件系统(FileSystem)统一管理文件
- 文件以树形结构进行管理(不是二叉树)
- 文件分为两种
- 存储数据的文件——普通文件(俗称的文件),在Window OS 下,以文件后缀(file suffiex)来标记这个文件存储的内容是什么内容(*.txt 普通文本 *.docx Word 文档)
- 管理树形结构组织数据的文件——目录/文件夹(directory/dir),以/结束,代表这个结点是目录
- 这个文件树只是一个逻辑结构,而不是硬盘上的物理结构
文件的路径
- 关于文件的路径(Path):根据一个规则,从文件树上唯一确定一个位置这个位置一定对应某个结点,但是这个结点可以不存在
路径的分类
- 绝对路径(absolute path):从一棵树的根节点出发描述的路径
- 相对路径(relative path) :从我们所在的位置出发,所描述的路径(我们的位置一定是一个目录,不能处在一个文件上)
每个进程都有一个当前工作目录,一般一个进程的启动目录就是当前的工作目录
- 在JAVA字符串中\不能直接写\,因为在字符串中反斜杠,表示转义的意思,所以在JAVA中\应该写成\\ 比如"E:\\JAVA代码"
- Windows中使用\作为路径分隔符,Linux使用/作为路基分隔符,JAVA是跨平台的语言,所以我们在代码中写/或者\\都是可以的,比较推荐使用/ "E:/JAVA代码"
- 其中.表示当前位置(目录),..表示回到当前位置的父节点(目录)
- 根节点的父节点还是自己
文件的路径
- 路径是树上找到一个结点的位置
- 路径并不表示文件一定存在
- 路径有绝对路径和相对路径
- 当前位置:进程的当前工作目录
- 文件字符串中的“/”,或者“\\”表示路径分隔符
- .和..的含义
- C:/Widows/等价于C:/Windows
关于文件的操作
- 文件系统中,以结点为单位进行操作
- 文件移动操作(文件的重命名,文件的剪切+粘贴):结点移动(重命名:移动到其他节点下)
- 文件复制操作(复制+粘贴):新建节点+内容的复制
- 目录的移动操作(目录重命名 ,目录剪切+粘贴):以该节点为根的一颗子树的移动
- 目录复制操:以该节点为根的一颗子树的复制
- 删除:默认情况下只能删除普通文件或者空目录,只能删除节点,不能删除子树
- 删除非空目录:对整颗树的删除
- 关于对子树的操作,不能直接对子树进行操作,必须转换为节点操作,比如对目录的复制就变为(深度或者广度)进行遍历节点
通过Java操作文件
文件数据=元数据+内容数据
Java操作文件类
File对象描述一个文件(结点,结点对应的文件是否存在并不一定)
构造方法
判断权限的方法
文件的比较
按照文件名进行排序
文件的创建
- 返回true 创建成功
- 返回false创建失败 (文件已经存在)
- 抛出IOException:发生IO异常(指定的目录不存在)
- 临时文件的创建
- Window下的默认路径C:\Users\用户名\AppData\Local\Temp
目录的创建
- mkdirszz可以创建当前路径中还不存在目录
文件的删除
- 返回true,删除成功
- 返回false,对应文件不存在导致的删除失败,非空目录的删除
- IOException其他情况失败(比如其他进程打开文件)
删除非空目录
- 先遍历(深度遍历或者广度遍历),删除这个目录下的所有子孙,让这个目录变成空目录
- 删除该目录
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
获取文件路径
- 修饰过发绝对路径就是没有.或者..这些东西
文件名的修改
总结
- JDK中只提出针对结点的操作,没有提供对子树的操作,对树的操作,需要我们自己写代码完成
- delete()不是进入回收站的删除,而是真正的删除,回收站的删除只是把文件放到了一个固定目录
- 删除非空目录,先将目录的文件删完,再删空目录
处理文本数据的背后知识
数据的其后的本质是二进制数据(有范围的整数),我们可以用各种编码规则来表示文本(图片,声音的背后也是数字),比如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来处理
字符写入
- Writer是抽象类,OutputStreamWriter是实现子类(做字符编码处理)
- 对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(); } } } } }
进行目录的复制
- 如果是目录,继续递归+目标的相对位置创建目录
- 如果是文件,就直接进行文件的复制
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); } } } } }