文件的概念
狭义的文件:
存储在硬盘上的数据,以文件为单位,进行组织;文件夹也叫做"目录",也是一种特殊的文件(文件里存储的也是文件)
这章笔记的内容都是指狭义的文件
广义的文件:
操作系统负责管理软硬件资源,操作系统往往会把这些资源统一抽象成文件进行管理-----"一切皆文件"
比如,有一个网卡,就把这个网卡抽象成一个"文件",创建了特殊的文件来表示网卡;从网卡接收数据,就读取这个文件,往网卡发送数据,就写这个文件
再比如,有一个键盘,想从键盘种读取数据,也是将键盘抽象成一个"文件"(stdin),读这个文件就能读取到用户输入的内容了
绝对路径与相对路径
绝对路径是指目录下的绝对位置,直接到达目标位置,通常是从盘符开始的路径,完整的描述文件位置的路径就是绝对路径
路径之间使用\(反斜杠)分隔,当然/(斜杠)也可以,一般推荐使用/(斜杠)来分隔
相对路径首先需要一个"基准路径"(现在的位置),以基准路径为起点,往下继续走直到到达目标路径的表示方式
什么是缓冲区
我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区
缓冲区一般分为输入缓冲区和输出缓存区,输入缓冲区是输入设备与内存通信的缓冲区,输出缓冲区是内存与输出设备通信的缓冲区
但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中
File类中的文件操作
文件操作是属于操作系统层面提供的一些API,不同的操作系统提供的API是不一样的,JAVA作为一个跨平台语言,为了统一代码,就在JVM中把不同系统的文件操作API进行了封装~~
构造File对象:
File 对象名 = new File("路径");
根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File类当中的常用方法:
修饰符及返回值类型 | 方法名 | 说明 |
---|---|---|
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) | 传递一个File对象,进行文件改名 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
文本文件与二进制文件
数据文件被分为文本文件或者二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到硬盘,就是二进制文件
如果要求在硬盘上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
文件内容的读写操作
把硬盘上的数据往内存上面写就是输入
把内存上的数据往硬盘上面写就是输出
文件内容的读写也叫做"流"(stream),计算机中的流其实是一种信息的转换,把文件内容的读写操作比喻成水流,java标准库在"流"的概念上,提供了一组类,完成文件内容的读写操作~~
读写文件分为两种:一种是字节流(按字节读和写),一种是字符流(按字符读和写)
InputStream和OutputStream是字节流的,Reader和Writer是字符流的,Scanner和PrintWriter也是字符流的
打开文件以后一定要记得close()文件,不然会出现文件资源泄露的情况!!!
字节流InputStream:
读数据,把数据从硬盘读到内存中,InputStream 只是一个抽象类,要使用还需要具体的实现类,使用FileInputStream
修饰符及返回值类型 | 方法名 | 说明 |
---|---|---|
int | read() | 读取一个字节的数据,返回 -1 代表已经完全读完了 |
int | read(byte[] b) | 把读到的内容放到字节数组b中,返回实际读到的字节数;返回 -1 代表以及读完了 |
int | read(byte[] b, int off, int len) | 从字节数组b下标为off位置开始读len个字节,返回实际读到的字节数;返回 -1 代表以及读完了 |
void | close() | 关闭字节流(关闭文件) |
FileInputStream:
InputStream的具体实现类
方法名 | 说明 |
---|---|
FileInputStream(File file) | 利用 File 构造文件输入流(打开文件) |
FileInputStream(String name) | 利用文件路径构造文件输入流(打开文件) |
public class Test2 {
public static void main(String[] args) throws IOException {
//打开文件
InputStream inputStream = new FileInputStream("bbb.txt");
//读取文件内容
while (true) {
int b = inputStream.read();
if (b == -1) {
break;
}
System.out.println(b);
}
//关闭文件
inputStream.close();
}
}
字节流OutputStream:
写数据,把数据从内存写入到硬盘中,OutputStream只是一个抽象类,要使用还需要具体的实现类,使用FileOutputStream
使用OutputStream写文件的时候,只要打开文件成功,就会把文件原有内容清空~~如果文件不存在会自动创建
修饰符及返回值类型 | 方法名 | 说明 |
---|---|---|
void | write(int b) | 写入一个字节的数据 |
void | write(byte[] b) | 把字节数组里的所有内容都写到文件中 |
int | write(byte[] b, int off, int len) | 把b字节数组从下标off位置开始写,写len个字节 |
void | close() | 关闭字节流(关闭文件),也有清空缓冲区的作用 |
void | flush() | 重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。 |
FileOutputStream:
OutputStream的具体实现类
方法名 | 说明 |
---|---|
FileOutputStream(File file) | 利用 File 构造文件输出流(打开文件) |
FileOutputStream(String name) | 利用文件路径构造文件输出流(打开文件) |
public class Test1 {
public static void main(String[] args) throws IOException {
//打开文件
OutputStream outputStream = new FileOutputStream("./bbb.txt");
//OutputStream只要打开文件成功,就会把文件原有内容清空,如果文件不存在会自动创建
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
//关闭文件
outputStream.close();
}
}
字符流Reader:
读数据,把数据从硬盘读到内存中,Reader 只是一个抽象类,要使用还需要具体的实现类,使用FileReader
修饰符及返回值类型 | 方法名 | 说明 |
---|---|---|
int | read() | 读取一个字符的数据,返回 -1 代表已经完全读完了 |
int | read(char[] b) | 把读到的内容放到字符数组b中,返回实际读到的字符数;返回 -1 代表以及读完了 |
int | read(char[] b, int off, int len) | 从字符数组b下标为off位置开始读len个字符,返回实际读到的字符数;返回 -1 代表以及读完了 |
void | close() | 关闭字符流(关闭文件) |
字符流FileReader:
Reader的具体实现类
方法名 | 说明 |
---|---|
FileReader(File file) | 利用 File 构造文件输入流(打开文件) |
FileReader(String name) | 利用文件路径构造文件输入流(打开文件) |
public class Test2 {
public static void main(String[] args) throws IOException {
//打开文件
Reader reader = new FileReader("./bbb.txt");
//读取文件内容
while (true) {
int ret = reader.read();
if (ret == -1) {
break;
}
char ch = (char) ret;
System.out.println(ch);
}
//关闭文件
reader.close();
}
}
Writer:
写数据,把数据从内存写入到硬盘中,Writer只是一个抽象类,要使用还需要具体的实现类,使用FileWriter
使用Writer写文件的时候,只要打开文件成功,就会把文件原有内容清空~~如果文件不存在会自动创建
修饰符及返回值类型 | 方法名 | 说明 |
---|---|---|
void | write(int b) | 写入一个字符的数据 |
void | write(char[] b) | 把字符数组里的所有内容都写到文件中 |
void | write(String b) | 写入b字符串数据 |
int | write(char[] b, int off, int len) | 把b字符数组从下标off位置开始写,写len个字符 |
int | write(String b,int off,int len) | 把b字符串从下标off位置开始写,写len个字符 |
void | close() | 关闭字符流(关闭文件),也有清空缓冲区的作用 |
void | flush() | 重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。 |
FileWriter:
Writer的具体实现类
方法名 | 说明 |
---|---|
FileWriter(File file) | 利用 File 构造文件输出流(打开文件) |
FileWriter(String name) | 利用文件路径构造文件输出流(打开文件) |
public class Test3 {
public static void main(String[] args) throws IOException {
//打开文件
Writer writer = new FileWriter("./bbb.txt");
//Writer只要打开文件成功,就会把文件原有内容清空,如果文件不存在会自动创建
writer.write("hello world");
//关闭文件
writer.close();
}
}
Scanner:
使用字符流进行读操作的时候,还可以使用Scanner来进行读取~~
public class Test4 {
public static void main(String[] args) throws IOException {
//打开文件
InputStream inputStream = new FileInputStream("./bbb.txt");
//使用Scanner作为一个字符流对象(传递字节流对象)
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
//关闭文件
inputStream.close();
}
}
PrintWriter:
使用字符流进行写操作的时候,还可以使用PrintWriter来进行写操作~~
public class Test5 {
public static void main(String[] args) throws IOException {
//打开文件
OutputStream outputStream = new FileOutputStream("./bbb.txt");
//使用PrintWriter作为一个字符流对象(传递字节流对象)
PrintWriter writer = new PrintWriter(outputStream);
writer.printf("a = %d\n",10);
writer.println();
writer.print("abc");
//关闭文件
outputStream.close();
}
}
所有代码都需要考虑close是否一定能被执行的问题:
public class Test6 {
public static void main(String[] args) throws IOException {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream("./bbb.txt");
PrintWriter writer = new PrintWriter(outputStream);
writer.printf("a = %d\n",10);
writer.println();
writer.print("hello world");
}finally {
outputStream.close();
}
}
}
-----------------------------------------------------------------------------------------
public class Test6 {
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./bbb.txt")) {
PrintWriter writer = new PrintWriter(outputStream);
writer.printf("a = %d\n",10);
writer.println();
writer.print("hello world");
}
}
}
文件操作的一些实例
一、扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件~~
操作:
1、先让用户输入要扫描的路径+要查找的词
2、遍历目录,找到名字匹配的文件
3、询问用户是否删除
public class Test7 {
public static void main(String[] args) throws IOException {
//从键盘输入目录路径
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的路径: ");
File rootDir = new File(scanner.next());
//判断目录路径是否存在
if (!rootDir.isDirectory()) {
System.out.println("您输入的目录不存在");
return;
}
//从键盘输入关键词
System.out.println("请输入要搜索的关键词: ");
String toDelete = scanner.next();
//遍历目录
scanDir(rootDir,toDelete);
}
//借助这个方法进行递归遍历
public static void scanDir(File rootDir, String toDelete) throws IOException {
File[] files = rootDir.listFiles();
if (files == null) {
//说明rootDir是一个空的目录
return;
}
//rootDir非空,循环遍历每个元素
for (File f : files) {
//当前rootDir是目录
if (f.isDirectory()) {
//递归里面的内容
scanDir(f,toDelete);
}else {
//不是目录,是普通文件,判定文件名是否符合要求,是否要进行删除
checkDelete(f,toDelete);
}
}
}
//删除操作
public static void checkDelete(File f, String toDelete) throws IOException {
if (f.getName().contains(toDelete)) {
System.out.println("该单词" + toDelete + "被" +
f.getCanonicalPath() + "包含了" + ",是否要删除? (Y/N)");
Scanner scanner = new Scanner(System.in);
String choice = scanner.next();
if (choice.equals("Y") || choice.equals("y")) {
f.delete();
}
}
}
}
二、普通文件的复制
操作:把第一个文件打开,把里面的内容逐个字节的读取出来,写入到第二个文件中即可~~
public class Test8 {
public static void main(String[] args) throws IOException {
//1.先输入要复制哪个文件(源文件),以及把这个文件复制到哪里去(目标文件)~~
Scanner scanner = new Scanner(System.in);
System.out.println("请输入源文件: ");
//srcFile 形如 d:/cat.jpg
File srcFile = new File(scanner.next());
System.out.println("请输入目标文件: ");
//destFile 形如 d:/cat2.jpg
File destFile = new File(scanner.next());
if (!srcFile.isFile()) {
System.out.println("输入的源文件有误");
return;
}
if (!destFile.getParentFile().isDirectory()) {
System.out.println("输入的目标文件有误");
return;
}
//2.打开源文件,按照字节读取内容,再依次写入到目标文件
try (InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)){
//读src的每个字节,写入到dest中
while (true) {
int ret = inputStream.read();
if (ret == -1) {
break;
}
outputStream.write(ret);
}
}
}
}
三、扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
操作:
1、先让用户输入要扫描的路径+要查找的词
2、遍历目录,找到文件名匹配与内容匹配的文件
public class Test9 {
public static void main(String[] args) throws IOException {
//1.输入路径和要查询的关键词
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的路径: ");
File rootDir = new File(scanner.next());
System.out.println("请输入要查询的词: ");
String toFind = scanner.next();
//2.递归的扫描目录
scanDir(rootDir,toFind);
}
public static void scanDir(File rootDir, String toFind) throws IOException {
File[] files = rootDir.listFiles();
if (files == null) {
return;
}
for (File f : files) {
if (f.isDirectory()) {
scanDir(f,toFind);
}else {
checkFile(f,toFind);
}
}
}
public static void checkFile(File f, String toFind) throws IOException {
//1.先检查文件名
if (f.getName().contains(toFind)) {
System.out.println(f.getCanonicalPath() + " 文件名中包含 " + toFind);
}
//2.再检查文件内容
try (InputStream inputStream = new FileInputStream(f)){
StringBuilder stringBuilder = new StringBuilder();
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNextLine()) {
stringBuilder.append(scanner.nextLine() + "\n");
}
if (stringBuilder.indexOf(toFind) > -1) {
System.out.println(f.getCanonicalPath() + " 文件内容包含 " + toFind);
}
}
}
}