文件
文件是一种在硬盘上存储数据的方式,操作系统帮我们把硬盘的一些细节都封装起来了,程序员只需要了解文件相关的接口即可,相当于操作文件就是间接的操作硬盘了
硬盘用来存储数据,和内存相比硬盘的存储空间更大,访问速度更慢,成本更低,持久化存储,操作系统通过“文件系统”这样的模块来管理硬盘
不同的文件系统管理文件的方式都是类似的
通过目录(directory,平常叫文件夹,专业术语叫目录)构成了N叉树的树形结构
我们在文件系统中都是通过路径来确定一个具体的文件
同样是一个cat.jpg文件,站在不同的基准目录上,查找的路径是不相同的
文件系统上存储的文件,具体来说又分成两大类
1.文本文件–存储的是字符
字符怎么定义呢?
有个表叫utf8,这个表上数据的组合就是字符
2.二进制文件–存储的是二进制数据
判断文本文件和二进制文件最简单的方式就是
直接用记事本打开,如果打开之后能看懂,就是文本,否则就是二进制
像word文档,ppt,excel这些,如果托到记事本上,都是二进制文件,虽然word文档里存的东西是汉字等可以看懂的内容,但是word文档不仅仅包含我们自己输入的内容,还有行间距,文字格式等众多内容
但是如果把excel的后缀改成csv格式,就是文本文件了
Java标准库中操作文件(对文件系统操作)
操作文件可以理解成通过java代码把你硬盘的文件删除修改创建等,也就是通过java代码操作文件系统
Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意, File 对象可以对应到一个真实存在的文件,也可以对应到一个不存在的文件
获取文件路径
public static void main(String[] args) throws IOException {
File file=new File("./test.txt");
System.out.println(file.getParent());//上级目录
System.out.println(file.getName());//文件名
System.out.println(file.getPath());//new File后面的是什么路径,就输出什么
System.out.println(file.getAbsolutePath());//工作目录拼接上当前目录 在idea中运行一个程序,工作目录就是项目所在的目录
System.out.println(file.getCanonicalPath());//对getAbsolutePath的这个路径进行了修饰,让这个路径没有多余的东西,比如那个.
}
创建文件
public static void main(String[] args) {
File file=new File("./test.txt");
System.out.println(file.exists());//在当前项目所在的路径下(D:\code\Java\java-related-code\File)没有test.txt这个文件,应该是false
System.out.println(file.isFile());//都没有这个文件了,这两行肯定都是false了
System.out.println(file.isDirectory());
}
但是我们可以先创建这个文件
文件跟目录的区别可以认为是文件是这个路径的重点,而目录下面还有别的路径
删除文件
public static void main(String[] args) {
File file=new File("./test.txt");
file.delete();
}
public static void main(String[] args) throws InterruptedException {
File file=new File("./test.txt");
//不是立刻删除,等到程序运行结束再删除
file.deleteOnExit();
Thread.sleep(2000);
//程序运行两秒会结束,所以这个文件在两秒之后删除
}
创建目录
单层目录
public static void main(String[] args) {
File file = new File("./testDir");
// mk => make dir => directory
// mkdir 一次只能创建一层目录. mkdirs 可以一次创建多级目录
file.mkdir();
//file.mkdirs();
}
多层目录
public static void main(String[] args) {
File file = new File("./testDir/111/222");
// mk => make dir => directory
// mkdir 一次只能创建一层目录. mkdirs 可以一次创建多级目录
//file.mkdir();
file.mkdirs();
}
文件重命名
public static void main(String[] args)
{
File file = new File("./test.txt");
File file2 = new File("./src/test.txt");//本来test.txt跟src是同级别的,现在把test.txt移动到src目录下面了
//因此文件重命名也可以做到移动文件的效果
file.renameTo(file2);
}
针对文件内容操作
Reader
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
// Reader 使用.
public class Demo6
{
public static void main(String[] args) throws IOException
{
// FileReader 构造方法, 可以填写一个文件路径(绝对路径/相对路径都行), 也可以填写一个构造好的 File 对象
// Reader reader = new FileReader("d:/test.txt");
// try {
// // 中间的代码无论出现啥情况, close 都能保证执行到.
// } finally {
// // 抛出异常, 或者 return, close 就都执行不到了~~
// reader.close();
// }
// 上述使用 finally 的方式能解决问题, 但是不优雅.
// 使用 try with resources 是更好的解决方案.
try (Reader reader = new FileReader("d:/test.txt"))
{
while (true)
{
char buf[] = new char[1024];
int n = reader.read(buf);//读到的有效字符的个数
if (n == -1)
{
// 读到文件末尾了.
break;
}
//实际只读了n个字符,小于n就行了
for (int i = 0; i < n; i++)
{
System.out.print(buf[i] + ",");
}
}
}
}
}
文件泄露相当于打开很多文件,使用完之后都不关闭,文件描述符表就满了,再打开新的文件,文件描述符就装不下了,这些文件就不知道上哪里去了,就造成了文件泄露
InputStream
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Demo7 {
public static void main(String[] args) throws IOException {
try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
while (true) {
byte[] buf = new byte[1024];
int n = inputStream.read(buf);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%x ", buf[i]);
}
String s = new String(buf, 0, n, "utf8");
//把buf数组的从0到n下标在string的构造方法中,通过utf8的编码转换,转换成人能看懂的字符串
System.out.println(s);
}
}
}
}
于是我们可以借助Scanner来完成上述操作
平时我们输入是
Scanner scanner=new Scanner(System.in);
那么system.in的类型也是一个InputStream
于是我们就可以
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class Demo8 {
public static void main(String[] args) throws IOException
{
try (InputStream inputStream = new FileInputStream("d:/test.txt"))
{
Scanner scanner = new Scanner(inputStream);
// 此时就是从 test.txt 这个文件中读取数据了!!
String s = scanner.next();
System.out.println(s);
}
}
}
write
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Demo9
{
public static void main(String[] args) throws IOException {
try (Writer writer = new FileWriter("d:/test.txt", true))
{
// write 是可以一次直接写一个字符串. 这个是非常方便的.
writer.write("hello java");
}
}
}
经典面试题
1.扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
import java.io.File;
import java.util.Scanner;
public class Demo10 {
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
// 1. 让用户输入一个目录. 后续的查找都是针对这个目录来进行的.
System.out.println("请输入要搜索的根目录: ");
File rootPath = new File(scanner.next());
// 2. 再让用户输入要搜索/要删除的关键词.
System.out.println("请输入要删除的关键词: ");
String word = scanner.next();
// 3. 判定一下当前输入的目录是否有效.
if (!rootPath.isDirectory()) {
System.out.println("您此时输入的路径不是合法目录!");
return;
}
// 4. 遍历目录. 从根目录出发, 按照 深度优先(递归) 的方式, 进行遍历
scanDir(rootPath, word);
}
public static void scanDir(File currentDir, String word) {
// 1. 先列出当前目录中都包含哪些内容.
File[] files = currentDir.listFiles();
if (files == null || files.length == 0) {
// 空的目录或者非法的目录
return;
}
// 2. 遍历列出的文件, 分两个情况分别讨论.
for (File f : files) {
// 加个日志, 方便看程序执行的过程.
System.out.println(f.getAbsolutePath());
if (f.isFile()) {
// 3. 如果当前文件是普通文件, 看看文件名是否包含了 word, 来决定是否要删除.
dealFile(f, word);
} else {
// 4. 如果当前文件是目录文件, 就递归执行 scanDir
scanDir(f, word);
}
}
}
private static void dealFile(File f, String word) {
// 1. 先判定当前文件名是否包含 word
if (!f.getName().contains(word)) {
// 此时这个文件不包含 word 关键词. 直接跳过.
return;
}
// 2. 包含 word 就需要询问用户是否要删除该文件?
System.out.println("该文件是: " + f.getAbsolutePath() + ", 是否要确认删除? (Y/N)");
String choice = scanner.next();
if (choice.equals("Y") || choice.equals("y")) {
f.delete();
}
// 如果是其他值, 都忽略.
}
}
2.进行普通文件的复制
import java.io.*;
import java.util.Scanner;
// 完成文件复制.
public class Demo11 {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
// 1. 输入路径并且合法性判定
System.out.println("请输入要复制的源文件路径: ");
String src = scanner.next();
File srcFile = new File(src);
if (!srcFile.isFile()) {
System.out.println("您输入的源文件路径非法!");
return;
}
System.out.println("请输入要复制到的目标路径: ");
String dest = scanner.next();
File destFile = new File(dest);
// 不要求目标文件本身存在. 但是得保证目标文件所在的目录, 得是存在的.
// 假设目标文件写作 d:/tmp/cat2.jpg, 就需要保证 d:/tmp 目录是存在的.
if (!destFile.getParentFile().isDirectory()) {
System.out.println("您输入的目标文件路径非法!");
return;
}
// 2. 进行复制操作的过程. 按照字节流打开.
try (InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
while (true) {
byte[] buffer = new byte[20480];
int n = inputStream.read(buffer);
System.out.println("n = " + n);
if (n == -1) {
System.out.println("读取到 eof, 循环结束. ");
break;
}
outputStream.write(buffer, 0, n);
}
}
}
}