Java文件操作和io全解析
目录
一:认识⽂件
1.1:文件的含义
1.2:树型结构组织 和 ⽬录
1.3:⽂件路径(Path)
1.4:文件的种类
二:java中怎么文件操作
2.1:File 概述
2.2:⽅法
三:⽂件内容操作
3.1:FileInputStream 概述
3.2:OutputStream 概述
3.3:字符流 read
3.3:字符流write
四:小练习
一:认识⽂件
1.1:文件的含义
我们先来认识狭义上的⽂件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进⾏数据保存时,往往不是保存成⼀个整体,⽽是独⽴成⼀个个的单位进⾏保存,这个独⽴的单位就被抽象成⽂件的概念,就类似办公桌上的⼀份份真实的⽂件⼀般。⽂件除了有数据内容之外,还有⼀部分信息,例如⽂件名、⽂件类型、⽂件⼤⼩等并不作为⽂件的数据⽽存在,我们把这部分信息可以视为⽂件的元信息。
1.2:树型结构组织 和 ⽬录
同时,随着⽂件越来越多,对⽂件的系统管理也被提上了⽇程,如何进⾏⽂件的组织呢,⼀种合乎⾃然的想法出现了,就是按照层级结构进⾏组织⸺也就是我们数据结构中学习过的树形结构。这样,⼀种专⻔⽤来存放管理信息的特殊⽂件诞⽣了,也就是我们平时所谓⽂件夹(folder)或者⽬录 (directory)的概念。
1.3:⽂件路径(Path)
如何在⽂件系统中如何定位我们的⼀个唯⼀的⽂件就成为当前要解决的问题,但这难不倒计算机科学家,因为从树型结构的⻆度来看,树中的每个结点都可以被⼀条从根开始,⼀直到达的结点的路径所描述,⽽这种描述⽅式就被称为⽂件的绝对路径(absolute path)。基准路径+相对路径(从基准目录出发)=绝对路径(从盘符出发)除了可以从根开始进⾏路径的描述,我们可以从任意结点出发,进⾏路径的描述,⽽这种描述⽅式就 被称为相对路径(relative path),相对于当前所在结点的⼀条路径。
1.4:文件的种类
1:文本文件(字符串,合法字符)
2:二进制文件(存啥都行)在计算机中一个汉字占几个字节?
根据字符集编码,ascil通过数字、表示英文字母/标点符号/阿拉伯数字
引入更大的码表,来表示汉字
1gbk 2个字节表示一个汉字(windows简体中文版,默认是gbk,然后vs的默认编码是跟随系统的)
2utf-8 utf8是变长编码,2-4个字节,大部分的汉字都是三个字节表示了
3java中,一个char用到utf16也是2个字节表示一个汉字,Stirng又通常回转换utf8的形式(内部的编码转换过程,使用过程中感知不到)
先查看文本是什么文件,把文件往记事本一拖,乱码就是二进制文件,正常就是文本文件。
二:java中怎么文件操作
1文件系统操作
创建文件,删除文件,移动文件,获取文件属性
file类
2文件内容操作
读文件,写文件
2.1:File 概述
属性
修饰符及类型 属性 说明
static String pathSeparator 依赖于系统的路径分隔符,String类型的表⽰
static char pathSeparator 依赖于系统的路径分隔符,char 类型的表⽰构造⽅法
签名 说明
File(File parent, String child) 根据⽗⽬录 + 孩⼦⽂件路径,创建⼀个新的 File 实例File(String pathname) 根据⽂件路径创建⼀个新的 File 实例,路径可以是 绝对路径或者相对路径
File(String parent, String child) 根据⽗⽬录 + 孩⼦⽂件路径,创建⼀个新的 File 实例,⽗⽬录⽤路径表⽰。
第二个方法常用:
2.2:⽅法
⽅法
运行:
运行:
运行:
运行:
这个方法还是很香的,就类似自动保存一样,我不用了他就可以关了。
list和listFIles()是针对目录来说
list查询目录里面的目录名
lisrFiles查询文件里面的目录的路径
mkdir()和mkdirs()创建一层目录和创建多层目录
renameTo
三:⽂件内容操作
读文件:硬盘的数据读到内存
写文件:把内存的数据写道硬盘
流对象来进行文件内容操作、
ava中通过一系列的类,表示流对象
分成两个大类:
1.字节流 (读写数据的时候,以字节为基本单位,一次最少读一个字节)
读写二进制文件,通常使用字节流
inputStream读
outputstream写
2.字符流(读写数据的时候,以字符为基本单位,一次最少读写一个字符)一个字符可能对应多个字节,看编码方式
读写文本文件,通常使用字符流
reader读
writer写
在io过程中,输入输出,指的是以cpu为基准,来进行考虑的
3.1:FileInputStream 概述
InputStream 概述
⽅法
修饰符及返回值类型 ⽅法签名 说明
int read() 读取⼀个字节的数据,返回 -1 代表已经完全读完了int read(byte[] b) 最多读取 b.length 字节的数据到 b中,返回实际读到 的数量;-1 代表以及读完了
int read(byte[] b, int off, int len) 最多读取 len - off 字节的数据到 b中,放在从 off开始,返回实际读到的数量;-1 代表以及读完了
void close() 关闭字节流
InputStream 只是⼀个抽象类,要使⽤还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输⼊设备都可以对应⼀个 InputStream 类,我们现在只关⼼从⽂件中读取,所以使⽤ FileInputStream
构造⽅法
代码⽰例
read的三个版本
⽰例1
将⽂件完全读完的两种⽅式。相⽐较⽽⾔,后⼀种的 IO 次数更少,性能更好
运行:在test.txt文本文档中写hello world
此处的bytes参数,称为输出型参数,实现构造好一个空的数组,不是null,而是里面的元素全是0的数组,由read方法内部,往参数数组中进行填充
n就是实际读到的有效字节数
read会尽可能把bytes给填满
然后这个代码还需要调整:
这个样子打印就可以啦
第三个版本
read(byte[] b, int off, int len)
也是尝试把数据放到b字节数组中,不是从开始放,而是从off(offset)下标开始,最多填充len个,只是使用数组的一部分。
读取完毕还需要关闭文件
inputstream.close();
对于java来说,我们有jvm的垃圾回收机制,自动的把不使用的内存释放掉。
文件资源泄露
进程PCB中一个属性,文件描述符表(顺序表),每次打开一个文件,就会在这个表里,插入一个元素,文件描述符表,长度是定长的(不能自动扩容),光打开文件,不关闭文件,此时就会逐渐把这个表里的内容消耗殆尽,后序再尝试打开,就会打开失败。
3.2:OutputStream 概述
outputStream提供了write方法其中有三中方法
OutputStream 同样只是⼀个抽象类,要使⽤还需要具体的实现类。我们现在还是只关⼼写⼊⽂件中,所以使⽤ FileOutputStream
第一个:写一个字节数组
文本文档:
第二个:写一个字节
查看text.txt文本文档:
就是99 100 101 102的ascall码
此处大家有没有疑惑,在同一个文本文档,当时操作了两次,那第二次是覆盖第一次还是?
答案是没有覆盖,而是清空,每次按照写的方式打开文件,就会清空文件原有内容
操作一下,如果我没有write方法:
文本文档:
每次先清空再写
但是但是肯定也有解决办法啦,让我们在原来的基础上接着写
就是在实例化的时候文件路径后面加上true如下:
我们先执行一次:
再执行一次:
3.3:字符流 read
read一次读一个字符
一次读若干个字符,填充到char[]返回实际读到的字符个数
一次读若干个字符,填充到char[]的一部分中
3.3:字符流write
可以字符串,字符,整型
追加写以还是可以适用的
总结:
字节流
inputStream fileinputStream
outputstram fileoutputStream
字符流
reader filereaderr
writer filewriter
四:小练习
扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否要删除该⽂件
package io;import java.io.File;import java.util.Scanner;public class Demo12 {public static void main(String[] args) {//1让用户输入查询的文件名Scanner scanner = new Scanner(System.in);System.out.println("请输入要搜索的目录");String dir = scanner.nextLine();System.out.println("请输入要查询的文件名");String filename = scanner.nextLine();//2判断目录是否存在File rootfile = new File(dir);if(!rootfile.isDirectory()){System.out.println("目录不存在");return;}//3进行搜索,递归的遍历目录中所有的文件和子目录search(rootfile,filename);}private static void search(File rootfile, String filename) {// TODO Auto-generated method stub//先列出目录和子目录中所有文件名File[] files = rootfile.listFiles();if(files==null){System.out.println("目录为空");return ;}//如果不为空,那就遍历files数组,判定每个数组类型for(File file :files){if(file.isDirectory()){search(file,filename);}else if(file.isFile()){if(file.getName().contains(filename)){tryDelete(file);}}}}private static void tryDelete(File file) {System.out.println("准备删除的文件"+file.getAbsolutePath());Scanner scanner =new Scanner(System.in);System.out.println("确认删除吗?(y/n)");String confirm = scanner.nextLine();if(confirm.equals("y")){file.delete();System.out.println("删除成功");}}}
运行结果:
⽰例2
进⾏普通⽂件的复制package io;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.Scanner;public class Demo13 {public static void main(String[] args) throws IOException{//拷贝一个文件//1输入原文件和目标文件路径Scanner scanner = new Scanner(System.in);System.out.println("请输入原文件路径");String srcpath = scanner.nextLine();System.out.println("请输入目标文件路径");String destpath = scanner.nextLine();//判断原文件是否存在File srcfile= new File(srcpath);if(!srcfile.isFile()){System.out.println("原文件不存在");return;}//判断目标文件的夫目录是否存在File destfile = new File(destpath);File parent = destfile.getParentFile();if(!parent.isDirectory()){System.out.println("目标文件的父目录不存在");return;}//2进行拷贝copy(srcfile, destfile);}private static void copy(File srcfile, File destfile) throws IOException{//支持打开多行流,自动关闭try(InputStream in = new FileInputStream(srcfile);OutputStream out = new FileOutputStream(destfile);){//进行拷贝while(true){byte[] buffer = new byte[1024];int len = in.read(buffer);if(len==-1){break;}out.write(buffer,0,len);}}catch(IOException e){e.printStackTrace();}}}
















































