书接上文
文本操作的方法
|------------|-------------|-----------------------------------|
| String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
| File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |





此处是针对File对象打印得到的效果(调用了File的toString)
|---------|----------|------------------------------|
| boolean | mkdir() | 创建 File 对象代表的目录 |
| boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
前者只能创建一层目录,后者可以一次性创建多层目录




|---------|---------------------|--------------------------|
| boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |




不光能重命名,还能够移动位置
文件内容操作
读文件:硬盘的数据读到内存中去
写文件:把内存的数据写到硬盘里
我们通过流对象进行文件内容操作
Java通过一系列的类来表示"流对象",一般分成两个大类
1.字节流:读写数据的时候,以字节为基本单位,一次最少读写一个字节。读写二进制文件的是偶,通常使用字节流
InputStream 读
OutputStream 写
2.字符流:读写数据的时候,以字符为基本单位,一次最少读写一个字符(一个字符可能对应多个字节),看编码方式。读写文本文件,通常使用字符流。
Reader 读
Writer 写
在IO操作过程中,输入输出指的是以CPU为基准来进行考虑的,从硬盘读取内容到内存中去,再从内存中读取内容放到CPU中。
*C语言中的"字符流"本质上是"字节流",C语言没有"Java的字符的概念",本身没有处理UTF-8,GBK等编码的能力,压根处理不了中文。C++也一样。

是一个抽象类,因此不能直接创建实例

java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class demo6 {
public static void main(String[] args) throws Exception {
// 创建文件,这个过程,对应到操作系统中,称为"打开文件"
InputStream inputStream = new FileInputStream("d:/test.txt");
while(true){
int c = inputStream.read(); // 读取一个字节,对应到操作系统中,称为"读取文件"
if(c == -1){ // 读取到文件末尾,返回 -1
break; // 跳出循环
}
System.out.printf("0x%x\n",c);
}
}
}

对照ASCII表:ASCII码对照表,ASCII码一览表(非常详细) - C语言中文网




操作系统中,流这个概念,不仅仅是能够搭配文件使用的,还有其他的用途(例如搭配网络进行使用)
站在操作系统的视角,如果想要读写文件,需要先打开文件。

无参数版本,一次只能读取一个字节

一个参数版本,尝试把这个参数中的字节数组给填满

三个参数版本,也是尝试把数据放到b字节数组中,而不是从头开始放,是从offset的下标开始最多填充len个,只使用了数组的一部分
若test里面存放的是"你好"


第二种写法:

此处的buffer参数,称为"输出型参数"。实现构造好一个空的数组,不是null,而是里面的元素全为0的数组,由read方法内部,往参数数组中进行填充。n表示实际读到的有效字节数。
一个方法可以看成是一个"工厂",参数是"原材料",返回值就是"产品"。
Java中一个方法,只能返回一个值,如果需要返回多个值,要么借助输出型参数,要么把返回的多个值打包成一个类。一个方法返回多个值,很多语言多支持,Pyhthon,Go......这种语法就完全不需要输出型参数。
java
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class demo7 {
public static void main(String[] args) throws IOException {
InputStream file = new FileInputStream("d:/test.txt");
byte[] buffer = new byte[1024]; // 缓冲区,用于存储读取到的字节
while (true) {
int n = file.read(buffer);
if (n == -1) { // 读取到文件末尾,返回 -1
break; // 跳出循环
}
for (int i = 0; i < n; i++) { // 循环 n 次,输出缓冲区中的字节
System.out.printf("0x%x\n", buffer[i]); // 输出字节的十六进制表示
}
}
}
}
内存泄漏
内存泄漏问题:内存申请了之后,没有释放,可用内存越来越少,后序内存都申请不了了。对于Java来说,JVM存在垃圾回收机制,能够自动的把不适用更大内存全部释放掉。
文件资源,也会涉及到"泄漏"
进程PCB中有一个属性,文件描述符表(这是一个顺序表),每次打开一个文件,就会再找个表里插入一个元素。问年间描述符表,长度是定长的(无法自动扩容)。光打开文件,不关闭文件,此时就会逐渐把找个表里的内容消耗殆尽,后序再尝试打开,就会打开失败。

进行修复之后

此时代码并不美观
继续修改得到:
java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class demo8 {
public static void readFile() throws IOException{
//利用try-with-resources自动关闭流
try(InputStream inputStream = new FileInputStream("d:/test.txt")){
byte[] buffer = new byte[1024]; // 缓冲区,用于存储读取到的字节
while (true) {
int n = inputStream.read(buffer); // 读取文件内容到缓冲区
if (n == -1) { // 读取到文件末尾,返回 -1
break; // 跳出循环
}
for (int i = 0; i < n; i++) { // 循环 n 次,输出缓冲区中的字节
System.out.printf("0x%x\n", buffer[i]); // 输出字节的十六进制表示
}
}
}catch(IOException e) {
e.printStackTrace(); // 打印异常信息
}
写文件OutputStream

写一个字节
写一个字节数组
写字节数组的一个部分
java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class demo9 {
public static void writeFile() {
try (OutputStream os = new FileOutputStream("d:/test.txt")) {
os.write(97);
os.write(98);
os.write(99);
os.write(100);
} catch (IOException e) {
e.printStackTrace(); // TODO: handle exception
}
// TODO Auto-generated method stub
}
public static void main(String[] args) {
writeFile();
}
}

每一次写,都将原来的文本内容清空重新开始写

加上append这个参数,表示"追加写",不会清空原有内容,新写入的内容就会从文件末尾继续追加。
文件读取

一次性只读取一个字符,-1表示结束
一次性读取若干个字符,填充到char[],返回实际读到的字符个数
一次性读取若干个字符,填充到 char[] 的一部分
java
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class demo10 {
public static void main(String[] args) throws FileNotFoundException {
try(Reader reader = new FileReader("d:/test.txt")){
while(true){
int ch = reader.read();
if(ch == -1){
break;
}
char c = (char)ch; // 强制类型转换,将 int 类型转换为 char 类型
System.out.print(c); // 输出字符
}
} catch (IOException e) {
e.printStackTrace(); // 打印异常栈信息,方便调试
// TODO: handle exception
}
}
}

write写文件操作

java
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class demo11 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("d:/test.txt",true)){ // 追加模式,true 表示追加到文件末尾,false 表示覆盖原文件内容
writer.write("Hello, World!"); // 写入字符串到文件
} catch (IOException e) {
e.printStackTrace();// TODO: handle exception
}
}
}


三个应用题
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
java
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 dirPath = scanner.next();
System.out.println("请输入要查询的文件名:");
String fileName = scanner.next();
//2.判定目录是否存在
File dir = new File(dirPath);
if(!dir.isDirectory()){
System.out.println("目录不存在");
return; // 终止程序
}
//3.进行搜索,递归的遍历目录中所有的文件和子目录
searchFile(dir,fileName); // 调用递归方法进行搜索
}
private static void searchFile(File dir, String fileName) {
// 1.获取目录中的所有文件和子目录
File[] files = dir.listFiles(); // 获取目录中的所有文件和子目录
if(files == null){ // 如果目录为空,直接返回
return; // 终止递归
}
//2.遍历files数组,判定每个元素的类型
for(File file : files){ // 遍历files数组,判定每个元素的类型
if(file.isDirectory()){ // 如果是目录,递归调用searchFile方法
searchFile(file,fileName); // 递归调用searchFile方法
}else{ // 如果是文件,判断文件名是否包含要查询的字符串
if(file.getName().contains(fileName)){
//如果找到了,那么就要尝试删除
trydelete(file); // 调用trydelete方法进行删除
}
}
}
}
private static void trydelete(File file) {
System.out.println("准备删除文件:"+file.getAbsolutePath());
Scanner scanner = new Scanner(System.in); // 创建Scanner对象,用于读取用户输入
System.out.println("是否要删除文件:" + file.getAbsolutePath() + "?(y/n)"); // 打印提示信息
String choice = scanner.next(); // 读取用户输入的字符串
if(choice.equals("y")){
file.delete();
System.out.println("文件删除成功");
}
}
}
进行普通文件的复制
java
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 Exception {
//1.输入源文件目录和目标文件目录
Scanner scanner = new Scanner(System.in);
System.out.println("请输入源文件目录:");
String src = scanner.next();
System.out.println("请输入目标文件目录:");
String dest = scanner.next();
//2.判定源文件是否存在
File srcFile = new File(src);
if(!srcFile.exists()){
System.out.println("源文件不存在");
return; //结束程序
}
//3.判定目标文件的父目录是否存在,如果不存在,则创建
File destFile = new File(dest);
if(!destFile.getParentFile().exists()){ //如果目标文件的父目录不存在,则创建
return;
}
//4.进行文件的复制
copy(srcFile,destFile);
}
//进行文件的复制
public static void copy(File srcFile,File destFile) throws Exception {
try(InputStream inputStream = new FileInputStream(srcFile); OutputStream outputStream =
new FileOutputStream(destFile)) {
while(true){ //循环读取文件内容,直到文件末尾
byte[] buffer = new byte[1024]; //创建一个缓冲区,用于存储读取到的文件内容
int len = inputStream.read(buffer); //读取文件内容到缓冲区中,返回读取到的字节数
if(len == -1){ //如果读取到的字节数为-1,则表示文件末尾
break; //结束循环
}
outputStream.write(buffer,0,len); //将读取到的文件内容写入到目标文件中,从缓冲区的0位置开始,写入len个字节
}
} catch (IOException e) {
e.printStackTrace(); // TODO: handle exception
}
}
}