文件操作和IO
什么是文件📦
-
平时所说的文件一般都是值存储在硬盘上的普通文件,形如:.txt/.jpg…
-
广义的说文件的概念,不仅仅包含普通文件,还可以包含目录,甚至是操作系统层面中用文件来描述一些其他的硬件设备或者软件资源
比如:网卡,当我们把网卡这样的硬件设备也抽象成一个文件,就可以简化开发
-
显示器键盘这些设备也都可以视作文件
文件(普通文件)的分类🌊
- 二进制文件:存储的是字节数据
- 文本文件:存储的是字符,本质存的还是字节数据,只不过若干字节能够组成字符
文件的目录结构🏷
在计算机中,保存和管理文件,是通过操作系统中的的文件系统这样的模块来负责.
文件系统中,一般是通过"树形"结构来组织磁盘上的目录和文件的,只不过不是二叉树,而是N叉树
描述文件/目录所在位置的方式Ⓜ️
-
绝对路径
以盘符做开头,如:D:\install\AnLinkSetUp\AnLinkSetuo.exe
-
相对路径
以.或者…做开头的,其中一个点.表示当前路径,而两个点…表示当前路径的父目录,也就是上级路径
谈到相对路径,那不得不提基准路径:如:以D:\program\jdk\bin做目录,找javac.exe
那: ./javac.exe中的点.就表示当前目录(基准目录)
若还是以D:\program\jdk\bin做基准目录,去找src.zip文件
那: …/src.zip
注意使用路径时表示分隔符的斜线要用斜杠/而不是反斜杠\,我们查看自己电脑的某文件路径时会看到,这是windows的历时遗留问题,我们不用管,只要管好自己的路径使用/就可以了
文件的操作⚡️
文件系统的操作🌵
此处指的是:对文件管理系统进行的一系列操作,这之中包括了:列出目录下有哪些文件/创建文件/删除文件/重命名文件…
我们需借助Java中提供的java.io.File类来对一个文件(包括目录)进行抽象的描述,需注意,有File对象并不表示就真的就这个文件了
如根据绝对路径和相对路径来创建File对象:
File file1=new File("d:/test.txt");
File file2=new File("./test.txt");
对于相对路径的形式来说,我们需要知道它的基准路径在哪:
此时我们应当关心java程序是怎么运行起来的
-
通过cmd窗口执行java程序时:如编译:javac -encoding utf-8 helloworld.java
那我们就必须明确,执行命令所在的目录就是基准目录,但是这种情况平时开发也不会用,所以不用过分关心
-
通过IDEA来进行
此时的基准目录就是项目所在路径.
第一组方法⚙️
返回值类型 | 方法名 | 说明 |
---|---|---|
String | getParent() | 返回File对象的父目录文件路径 |
String | getName() | 返回File对象的文件名称(带后缀) |
String | getPath() | 返回File对象的文件路径,这里跟我们构造File对象写的路径一致 |
String | getAbsolutePath() | 返回File对象的绝对路径(简单的字符串拼接) |
String | getCanonicalPath() | 返回File对象的经过修饰的绝对路径 |
对于getAbsolutePath()和getCanonicalPath()的区别:
当使用相对路径的形式构造一个File对象是时:前者是将从盘符开始的基准路径对应的字符串和构造时使用的相对路径进行简单的字符串拼接,此时可能会有\.\的字符串样式出现,这样显然不合适,由此就有了getCanonicalPath(),就被修饰成一个完整且没有多余符号的一个绝对路径了
第二组方法🗡
返回值类型 | 方法名 | 说明 |
---|---|---|
boolean | exists() | 判断当前的File对象对应的文件是否真实存在 |
boolean | isDirectory() | 判断当前的File对象对应的文件是不是一个目录 |
boolean | isFile() | 判断File对象对应的文件是不是一个普通文件 |
第三组方法⛵️
返回值类型 | 方法名 | 说明 |
---|---|---|
boolean | createNewFile() | 根据File对象,创建一个空文件,成功创建后返回一个true |
boolean | delete() | 根据File对象,删除该文件,成功删除后返回true |
void | deleteOnExit() | 根据File对象,标注的文件将会被删除,但删除动作会在JVM运行结束之后才会进行 |
演示:
public class Demo1 {
public static void main(String[] args) throws IOException {
File file =new File("./text.txt");
System.out.println(file.exists());
file.createNewFile();
System.out.println(file.exists());
file.delete();
System.out.println(file.exists());
}
}
打印:
false
true
false
第四组方法💇
返回值类型 | 方法名 | 说明 |
---|---|---|
String[] | list() | 返回File对象代表的目录下的所有文件名(字符串) |
File[] | listFiles() | 返回File对象代表的目录下的所有文件,以File对象的形式返回 |
boolean | mkdir() | 创建File独享代表的目录 |
boolean | mkdirs() | 创建File对象代表的目录,如果有必要,会创建中间目录 |
演示:
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("./aaa");
System.out.println(Arrays.toString(file.list()));//因为aaa是一个空目录,所以此处返回[]
file.mkdir();//项目所在路径下回创建一个aaa的目录
File file1=new File("./bbb/ccc/ddd");
file1.mkdirs();//项目所在路径会创建多级目录
File file2=new File("./");
System.out.println(Arrays.toString(file2.listFiles()));//File对象要表示出路径
System.out.println(Arrays.toString(file2.list()));//只是文件名
}
}
打印:
[]
[.\.idea, .\2022.5.20.1.iml, .\aaa, .\bbb, .\out, .\src]
[.idea, 2022.5.20.1.iml, aaa, bbb, out, src]
第五组方法🎃
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切/粘贴操作 |
---|
演示:
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("./xxx");
File file1 = new File("./zzz");
file.renameTo(file1);
}
}
说明:
xxx文件存在则会被改名
不存在不会有任何影响
文件内容的读写🚛
- 打开文件
- 读文件
- 写文件
- 关闭文件
Note:input就是输入嘛,可以理解成输入到我们的程序,而从我们的程序向外界输出就是所谓的output,与此对应就是,read就是读到程序里,write到外面去
Inputstream的方法📧
返回值类型 | 方法名 | 说明 |
---|---|---|
int | read() | 一次读取一个字节的内容,返回-1就是读取结束,正常返回的是那个字节所表示的值 |
Int | read(byte[] b) | 读到b中,因为b有一定的大小,所以最多读取b.length个字节的数据进去,返回实际读到的字节的数量,返回-1就是读取结束 |
int | read(byte[] b,int off,int len) | 同理,最多读取len-off个字节进b,从off下标处开始放,返回实际读到的字节的数量,-1表示读取结束 |
void | close() | 关闭字节流 |
FileInputStream🌛
-
构造方法
方法名 说明 FileInputStream(File file) 利用file构造文件输入流 FileInputStream(String name) 利用文件路径构造文件输入流 -
演示:
public class Demo1 { public static void main(String[] args) throws IOException { InputStream inputStream = new FileInputStream("./test.txt"); while(true){ int b=inputStream.read(); if(b==-1){ System.out.println("文件读取结束"); break; } System.out.println(b); } inputStream.close(); } } //test.txt中有abcdef 打印: 97 98 99 100 101 102 文件读取结束
public class Demo1 { public static void main(String[] args) { try(InputStream inputStream = new FileInputStream("./test.txt")){ while(true){ /** * 此处可以理解为b就是一个容器,每次最多搬运1024个字节的数据进来进行打印,文件内容总有搬完的时候 * 所以b的大小随意 * * */ byte[] b =new byte[1024]; int len = inputStream.read(b); if(len==-1){ System.out.println("文件读取结束!"); break; } for(int i=0;i<len;i++){ System.out.println(b[i]); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } //test.txt中有abcdef 打印: 97 98 99 100 101 102 文件读取结束
带三个参数的版本也差不多,不再赘述,需注意:像换行这样的字符也会被读进来哦!~
FileOutputStream🎋
可以看出,write()也是有三个版本.
写的话就不用太关注其返回值了.
但是要注意,上述写操作会清空文件中原有的内容!!!
这样就可以追加内容了.
Reader和Writer都完全类似.不再赘述.
利用Scanner进行读取👶
演示:
public class Demo1 {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("./test.txt")) {
try(Scanner scanner=new Scanner(inputStream,"utf-8")){
//1.打开流
//2.用scanner去接那个水流,并且用utf-8的字符集去翻译它
while(scanner.hasNext()){
String ret=scanner.next();
System.out.println(ret);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节流和字符流的桥接:OutputStreamWriter🎯
当我们想写字符数据,并以字节流的方式进行存储时,可能需要用到这个,演示:
public class Demo1 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("./test.txt",true)){
//写入它的字符将会用指定的字符集编码为字节字符集
try(OutputStreamWriter writer = new OutputStreamWriter(outputStream,"utf-8")){
//PrintWriter就是为了我们方便使用格式化输出,提供了print,println,printf方法,都是我们所熟悉的
try(PrintWriter printWriter =new PrintWriter(writer)){
printWriter.print("哈哈");
printWriter.println("xixi");
printWriter.flush();//写的时候最好把缓冲区的内容都冲刷走,哈哈
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件操作案例💯
- 给一个文件目录,去删除里头一个指定文件名的文件(也有可能找不到)
public class Demo1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入需要扫描的文件名:");
String scanDir= sc.next();
File file = new File(scanDir);
if(!file.isDirectory()){
System.out.println("指定的扫描文件并不是一个目录文件,扫描失败!");
return;
}
System.out.println("请输入需要删除的文件名:");
String toDel=sc.next();
scan(file,toDel);
}
private static void scan(File file,String toDel) {
Scanner sc = new Scanner(System.in);
File file1=new File(toDel);
File[] files=file.listFiles();
for(int i=0;i<files.length;i++){
if(files[i].isFile()){
if(files[i].getName().contains(toDel)){
try {
deleteFile(files[i]);
} catch (IOException e) {
e.printStackTrace();
}
}
}else{
scan(files[i],toDel);
}
}
}
private static void deleteFile(File file) throws IOException {
System.out.println("查找到指定文件:");
System.out.println(file.getCanonicalPath());
System.out.println("是否确认删除? 1:删除 0:取消");
Scanner scanner = new Scanner(System.in);
int choice=scanner.nextInt();
if(choice==1){
file.delete();
System.out.println("删除成功!");
}else{
System.out.println("取消成功!");
}
}
}
note:构造File对象的路径字符串应该和我们输入的路径一致.
2.文件复制操作
比如我们去复制一张照片
public class Demo1 {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("校徽.jpg")){
try(OutputStream outputStream = new FileOutputStream("复制品.jpg")){
while(true){
byte[] b = new byte[1024];
int len=inputStream.read(b);
if(len==-1){
System.out.println("文件复制成功!");
break;
}
outputStream.write(b,0,len);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//此处我们的项目路径下只有一张校徽.jpg文件,所以我们不断的读取改文件的字节数据,输出到复制品中就可以了
//复制品在运行之前没有的话可以自动创建,有的话也没事,里面东西都会被清空,再拷贝数据进去