文章目录
文件的概念
本节讨论的文件是指硬盘上的文件,操作系统管理文件,引入了专门的模块:文件系统。每个文件都有一个“路径”描述文件所在位置。
绝对路径:从盘符出发,到文件名结束。
相对路径:有一个基准路径,以这个基准做为参考,.表示当前目录,…表示当前目录的上一级目录。
文本文件:存储的内容虽然是二进制数据,但是这些二进制能从对应的字符集码表中查找出来翻译成对应的合法字符。
二进制文件:存储的是二进制数据,在字符集码表中查不出对应字符。
文件操作
1.文件系统的操作
文件操作包括:创建文件、删除文件、创建目录、重命名,在Java中提供了File类,来进行文件操作。
File概述:
属性
在Java中,pathSeparator 是一个表示路径分隔符的常量,定义在 java.lang.System 类中。它用于在文件路径中分隔多个路径。pathSeparator 的作用是帮助程序跨平台兼容不同的文件系统路径分隔符。
构造方法:
这里我们常用的是第二个构造方法.
方法:
修饰符及返回类型 | 方法签名 | 说明 |
---|---|---|
String | getParent() | 返回 File 对象的⽗⽬录⽂件路径 |
String | getName() | 返回 FIle 对象的纯⽂件名称 |
String | getPath() | 返回 File 对象的⽂件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的⽂件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的⽂件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的⽂件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,⾃动创建⼀个空⽂件,成功返回true |
boolean | delete() | 根据 File 对象,删除该⽂件。成功删除返回true |
void | deleteOnExit() | 根据File对象,删除文件,在程序运行结束才会删除 |
String[] | list() | 返回File对象代表的目录下的所有的文件名 |
File[] | listFiles() | 返回File对象代表的目录下的所有的文件,以File对象的形式返回 |
boolean | mkdir() | 创建File对象代表的目录 |
boolean | mkdirs() | 创建File对象代表的目录,必要时会创建中间目录 |
boolean | renameTo(File dest) | 进行文件名修改 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
代码案例:
案例1:getParent()、getName()、getPath()、getAbsolutePath()、getCanonicalPath()的使用
public class IODEmo1 {
public static void main(String[] args) throws IOException {
File file = new File(".//text.txt");
System.out.println("File对象的父目录文件路径: " + file.getParent());
System.out.println("File对象的文件名称: " + file.getName());
System.out.println("File对象的文件路径: " + file.getPath());
System.out.println("File对象的绝对路径: " + file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}
案例2:createNewFile()的使用、exists()、isFile()、isDirectory()的使用
File file=new File(".//text.txt");
//根据 File 对象,⾃动创建⼀个空⽂件,成功返回true
boolean ret=file.createNewFile();
System.out.println(ret);
由于已经成功插入返回true,再次调用会返回false.
案例3:list的使用
public class IODemo5 {
public static void main(String[] args) {
File file=new File(".");
//返回File对象代表的目录下的所有的文件名,返回值类型String[]
System.out.println(Arrays.toString(file.list()));
}
}
案例4:mkdir和mkdirs的使用
public class demo5 {
public static void main(String[] args) {
File f1 = new File("./abc");
File f2 = new File("./abc/def/ghi");
// boolean ok = f1.mkdir();//创建目录,只能创建一个
boolean ok = f2.mkdirs();//创建目录,可以创建多个子目录
System.out.println(ok);
}
}
案例6:renameTo的使用,renameTo既可以修改文件名,也可以移动目录
public class demo6 {
public static void main1(String[] args) {
File srcFile = new File("./abc");
File destFile = new File("./abc11");
boolean ok = srcFile.renameTo(destFile);//修改srcFile的文件名,修改成destFile
System.out.println(ok);
}
public static void main(String[] args) {
File srcFile = new File("./abc/def");
File destFile = new File("./def");
//相当于移动文件,把当前目录下的abc目录下的def,移动到当前目录下
boolean ok = srcFile.renameTo(destFile);//修改srcFile的文件名,修改成destFile
System.out.println(ok);
}
}
2.文件内容的操作
文件内容操作主要包括读文件和写文件,Java中对这些操作进行了封装,叫:文件流/IO流,这个叫法也是跟随操作系统的。
Java中将IO流分成两个:
1、字节流:
读写数据的基本单位是字节,一次只能读写一个字节。主要有InputStream、OutputStream这两个类,分别是输入、输出的字节流。
2、字符流:
读写数据的基本单位是字符,一次只能读写一个字符(一个字符是多少个字节不确定,取决于编码方式,字符流内部会自动查询码表,把二进制转换成对应的字符)。主要有Reader、Writer这两个类,分别是输入、输出的字符流。
我们谈的输入、输出是针对cpu来说的,数据远离cpu就是输出、数据靠近cpu就是输入。
InputStream、OutputStream、Reader、Writer都是抽象类,如果要使用,就得使用实现了上述4个抽象类的类,主要了解:FileInputStream、FileOutputStream、FileReader、FileWriter 这4个类即可。下面我会给出使用案例
小知识:
文件资源泄漏:打开文件,其实是在该进程中的"文件描述表"中,创建了一个新的表项,文件描述表描述了该进程需要操作哪些文件,文件描述表可以认为是一个数组,数组的每个元素包含了操作的文件的信息,而数组的下标就称为文件描述符,每次打开一个文件就相当于在数组中占据一个位置,数组长度是固定的,不能扩容,必须调用close才能释放空间,否则当数组满了,后面再打开文件就会失败。
为了避免上述问题,防止忘记关闭文件,我们可以将使用try catch和finally,将close操作放在finally中,另外还需要注意的一点是,在创建InputStream对象的同时,会有一个隐藏操作:打开文件,所以并不需要我们手动打开文件(请看案例1~)。
案例1:
public class demo1 {
public static void main1(String[] args) throws IOException {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./test.txt");//创建对象,创建对象的同时会有一个隐含的操作:打开文件
} catch (IOException e) {
e.printStackTrace();
} finally {
inputStream.close();//关闭文件
}
}
}
如果我们通过try-finally方法关闭文件,虽然有效果但是会显得很繁琐,于是Java中的try方法还提供了try with resources 如下方代码:
//改善方法
//Java try还提供了try with resources这样的方法,在try后边的括号中直接new,出了大括号try会自动调用close方法
//try括号中new的必须要实现closeable接口才可以
try (InputStream inputStream=new FileInputStream("./text.txt")){
byte[] buffer=new byte[1024];
while (true){
int n=inputStream.read(buffer);
if (n==-1){
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%x",buffer[i]);
}
}
}
}
使用try with resources会在出了大括号之后自动帮你关闭文件,在try()括号中的类必须要实现Closeable接口才可以
public abstract class InputStream implements Closeable
InputStream
每次读写最小的单位是字节.站在cpu的角度就是迎面向我们走来
public class IODemo6 {
public static void main(String[] args) throws IOException {
//使用try with resources 辅助我们关闭文件
try (InputStream inputStream = new FileInputStream("./text.txt")) {
while (true) {
//输出型参数
byte[] buffer = new byte[1024];
int n = inputStream.read(buffer);
//证明读取完毕
if (n == -1) {
break;
}
String s = new String(buffer, 0, n);
System.out.printf(s);
}
}
}
}
代码分析:
1.使用try with resources辅助我们关闭文件(try()括号中的类必须是实现Close接口的)
2 InputStream inputStream = new FileInputStream
3因为InputStream是抽象类,不能实例化,所以只能实例化FileInputStream
4输出型参数:将byte数组传进read方法中,read()方法将值存入到byte数组中
5如果读到文件末尾,继续read方法读取的话就会返回-1.
6.构造字符串,将值打印出来
这里的read方法相当于我们去食堂吃饭
1.没有参数的:相当于我们空着手去吃饭,食堂阿姨会给我一个盘子用来装
2.带一个参数的是相当于我们自己带一个饭盒子过去,让阿姨把饭装在我们自己的盒子里
3.带三个参数的:相当于我们指定让食堂阿姨将饭菜放到饭盒的指定格子中
OutStream
每次读写最小的单位是字节.站在cpu的角度就是离cpu远去.
这里的write()方法也是带有三个版本的,注意传参的类型是byte.
public class IODemo7 {
public static void main(String[] args) {
try (OutputStream outputStream=new FileOutputStream("./text.txt",true)){
byte[] buffer=new byte[]{91,92,93,94,95};
outputStream.write(buffer);
}catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileOutputStream(“./text.txt”)
当我们创建文件时,OutputStream 默认会将文件原来的内容都清空
如果我们不想清空原来的内容,而是在原有的基础上再次加新内容,就需要在文件名字后面加上true.
FileOutputStream(“./text.txt”,true))
Reader
public class IODemo8 {
public static void main(String[] args) {
try (Reader reader=new FileReader("./text.txt")){
while (true){
char[] buffer=new char[1024];
int n=reader.read(buffer);
if (n==-1){
break;
}
String s=new String(buffer,0,n);
System.out.println(s);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Reader的read方法,也有3个版本(注意这里传参的类型是char)
read();一次读取一个字符,返回值int
read(char[] cbuf);一次IO把读取的字符填充到字符数组中,返回实际读取到的个数(int)
read(char[] cbuf,int off,int len);把读取的字符填充到数组的[off,off+len)范围
Writer
public class IODemo9 {
public static void main(String[] args) {
try(Writer writer=new FileWriter("./text.txt")) {
String s="abc";
writer.write(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
write也有四个版本
write(int c);写一个字符到文件
write(String str);写一个字符串到文件
write(String str,int off,int len);将字符串的[off,off+len)的范围的内容写入文件中
write(char[] cbuf);写入字符数组中的内容到文件中
write(char[] cbuf,int off,int len);写入字符数组中[off,off+len)的范围的内容到文件中
InputStream对象作为Scanner的参数
public class demo6 {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("./test.txt")) {
Scanner scanner = new Scanner(inputStream);//Scanner也能传InputStream
while (scanner.hasNext()) {
System.out.println(scanner.next());//通过Scanner读取文件内容
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
综合案例
案例1:给定一个文件名,去指定目录中搜索并且返回路径
package IO;
import java.io.File;
import java.util.Scanner;
public class IODemo12 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入搜索的文件名");
String FileName=scanner.next();
System.out.println("请输入文件路径");
String rootPath=scanner.next();
File rootfile=new File(rootPath);
if (rootfile.isDirectory()){
System.out.println("路径有误");
return;
}
scanDir(rootfile,FileName);
}
private static void scanDir(File rootfile, String fileName) {
File[] files=rootfile.listFiles();
if (files==null){
return;
}
for(File f:files){
if (f.isFile()){
if (fileName.equals(f.getName())){
System.out.println("找到了"+f.getAbsolutePath());
}else if(f.isDirectory()){
scanDir(f,fileName);
}else {
;
}
}
}
}
}
代码分析
我们可以通过递归来搜索
首先,我们先判断当前路径的目录是否为空,为空就返回
通过for-each遍历,首先判断当前是否是文本文件,如果是,就对比
如果是目录就继续递归.