目录
一.什么是文件
平时我们锁说到的文件一般指的都是存储在硬盘上的普通文件,形如txt,png,jpg,等等这些都是普通文件,都存储在硬盘上.而在计算机中,文件可能是一个广义的概念,就不再是只包含普通文件,还可以包含目录(也就是"文件夹"一般把目录称为目录文件),而在这里我们主要针对普通文件来展开说!
这个就是存储普通文件的机械硬盘,机械硬盘的基本构造:1.盘片,存储数据的介质2.磁头
机械硬盘的运作:机械硬盘一旦上电,里面的盘片就会高速运转,磁头就会在盘片上找到对应的数据
另外还有固态硬盘(SSD),固态硬盘的结构和机械硬盘的是截然不同的,而固态硬盘的读写速度要比机械硬盘高很多,但固态硬盘会更贵一些
二.文件的分类
站在程序员的角度上,我们一般把文件分成两类:文本文件(存储字符)和二进制文件(存储字节)
这两种文件在编程上也会存在一定差异
简单的判断文件方法:用记事本打开,如果是乱码就是二进制文件,不是乱码就是文本文件
像这样的乱码就是二进制文件
关于目录结构
计算机中,保存管理文件,是通过操作系统中的"文件系统"这样的模块来负责的,文件系统中一般是通过"树形"(N叉树)结构在组织磁盘上的目录和文件的
整体的文件系统就是这种树形结构来存储的,如果是普通文件就是树的叶子结点,如果是目录文件,目录中就可以包含子树,这个目录就是非叶子结点,这个树每个节点的子树都可以有N个,这就是一个N叉树了
而在操作系统中就通过"路径"这样的概念,来描述一个一个具体文件/目录的位置~
路径这里有两种描述风格:
1.绝对路径
绝对路径指的就是一个具体的路径,以盘符开头的,例如E:\NEW\test.txt这样的路径,可以直接找到文件的路径
2.相对路径
以.或者..开头的,其中.表示当前路径..表示当前路径的父目录(上级路径),相对路径就必须要有一个基准目录,相对路径就是从基准目录出发,按照基准路径出发来找到相对应的文件,
例如以C:\Program Files\Java\jdk1.8.0_192\bin为基准路径,找到javac.exe,./javac.exe其中.就代表当前的基准路径,而要找到src.zip就可以这样写../src.zip,..就相当于回到了基准的上一层路径(C:\Program Files\Java\jdk1.8.0_192),再来找目标路径
即使定位到同一个路径,但基准路径不同的话,相对路径也是不同的!
三.Java中的文件操作
Java中的文件操作主要包含两类操作:
1.文件系统相关的操作
指的是通过"文件资源管理器"能够完成的一些功能
①列出目录中有哪些文件②创建文件③创建目录④删除文件⑤重命名文件...
Java中提供了一个File类,通过这个类就可以完成上述的操作,File类就描述了一个文件/目录,基于这个对象就可以实现上述的功能
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File f = new File("E:/NEW/test.txt");//构造函数传入了一个路径,这个路径可以使相对路径也可以是绝对路径,这里是绝对路径
System.out.println(f.getParent());//获得当前的父目录
System.out.println(f.getName());//获取文件名
System.out.println(f.getPath());//获取文件的路径(构造file的路径)
System.out.println(f.getAbsolutePath());//获取绝对路径
System.out.println(f.getCanonicalPath());//获取绝对路径 会有异常,表示输入输出的异常
System.out.println("============");
File f1 = new File("./test.txt");
System.out.println(f1.getParent());//获得当前的父目录
System.out.println(f1.getName());//获取文件名
System.out.println(f1.getPath());//获取文件的路径(构造file的路径)
System.out.println(f1.getAbsolutePath());//获取绝对路径 拼接基准路径和相对路径,但是这里的点表示的仍然是当前路径,因此我们可以完全不要这个路径
System.out.println(f1.getCanonicalPath());//获取绝对路径 化简过的绝对路径
}
}
判断文件是否存在
import java.io.File;
public class Demo2 {
public static void main(String[] args) {
File f = new File("E:/NEW/test.txt");
System.out.println(f.exists());//是否存在
System.out.println(f.isDirectory());//是否是目录
System.out.println(f.isFile());//是否是文件
}
}
创建文件
import java.io.File;
import java.io.IOException;
public class Demo3 {
public static void main(String[] args) throws IOException {
File f = new File("./text.txt");
System.out.println("文件是否存在 " + f.exists());
System.out.println("创建文件");
f.createNewFile();
System.out.println("创建文件结束");
System.out.println("文件是否存在 " + f.exists());
}
}
删除文件
import java.io.File;
public class Demo4 {
public static void main(String[] args) {
File f = new File("./text.txt");
f.delete();//删除文件(立即删除)
System.out.println("完成删除");
}
}
创建目录
import java.io.File;
public class Demo5 {
public static void main(String[] args) {
File f = new File("./aaa");
f.mkdir();//创建目录
}
}
import java.io.File;
public class Demo5 {
public static void main(String[] args) {
File f = new File("./aaa/bbb/ccc/ddd");
f.mkdirs();//创建多级目录
}
}
查看目录下的所有内容
import java.io.File;
import java.util.Arrays;
public class Demo6 {
public static void main(String[] args) {
File f = new File("./");
System.out.println(Arrays.toString(f.list()));//查看此目录下的内容(列出目录内容)
System.out.println(Arrays.toString(f.listFiles()));//按照file对象的方法打印
}
}
改名字
import java.io.File;
public class Demo7 {
public static void main(String[] args) {
File f = new File("./aaa");
File f1 = new File("./zzz");
f.renameTo(f1);//改名字,将f的名字改成f1的内容
}
}
上面这些操作都非常简单,也是我们常常使用的,因此还是需要掌握的
2.文件内容相关的操作
①打开文件
②读文件
③写文件
④关闭文件
针对文件内容的读写,Java标准库提供了一组类
按照文件的内容,分成了两个系列:
1)字节流对象,针对二进制文件,以字节为单位进行读写的
读:InputStream
写:OutputStream
2)字符流对象,针对文本文件,是以字符为单位进行读写的
读:Reader
写:Writer
上面四组类都是抽象类(这些抽象类既可以针对普通文件的读写,也可以针对特殊文件(网卡,socket)来进行读写),都是不能具体实现的,要实现的话往往是这些类的子类来实现:FileInputStream,FileOutputStream,FileReader,FileWriter,这些都是特指针对普通文件进行读写的
InputStream
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Demo8 {
public static void main(String[] args) {
InputStream inputStream = null;
try {//创建对象,也是打开文件,有可能没有文件,因此需要抛异常
inputStream = new FileInputStream("E:/NEW/test.txt");//需要指定路径,可以是相对路径也可以是绝对路径,也可以是File对象
//尝试一个一个字节的读,把整个文件读完
while(true){
int x = inputStream.read();
if(x == -1){
break;//表示读完了所有的字节
}
System.out.println(x);//打印
}
} catch (IOException e) {//这个包含了FileNotFoundException(子类),因此可以合并,IO操作失败的可能性是非常大的
e.printStackTrace();
}finally {
//读完之后需要关闭文件,释放资源
try {
inputStream.close();//这个放在finally里面更好有需要把定义放到外面去
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这个read()方法有三个版本的重载,无参数版本一次读一个,返回值为int,不是byte(-128~127,这个后面无法判断是否读完了所有字节),返回值的范围是-1~255,因此需要使用更大的类型来表示返回值,这里的-1就表示已经读完了所有字节,字节流结束了
一个参数版,一次读若干个字节把读的结果仿造参数中指定的数组中去,返回的就是读到的字节数
三个参数版本,一次读若干个字节把读的结果仿造参数中指定的数组中去,返回的就是读到的字节数,不是从起始位置读起,而是从中间位置读起,最后一个表示能放多少个元素我们可以看到这里写的还是挺麻烦的,其实Java里面提供了一个语法,可以在try里面自己关闭文件:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
public class Demo9 {
public static void main(String[] args) {
//这里要放到try里面,得实现Closeable接口,才可以自动关闭,而所有的流对象都有这个接口
try(InputStream inputStream = new FileInputStream("E:/NEW/test.txt");) {//这样就可以不用写close语句,try就会隐式的关闭文件,这样写就会简单很多
while(true){
/*int x = inputStream.read();//一次读一个
if(x == -1){
break;//表示读完了所有的字节
}*/
byte[] b = new byte[1024];
//这个会更常见一些比一次读一个更高效一些
int x = inputStream.read(b);//一次读多个,这里x的含义表示方法的返回值,这种做法称为"输出型参数",表示数组有多少个元素,
if(x == -1){
break;//表示读完了所有的字节
}
for(int i = 0;i < x;i ++){
//打印
System.out.println(b[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
OutputStream
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo10 {
public static void main(String[] args) {
try (OutputStream outputStream = new FileOutputStream("E:/NEW/test.txt")) {
/*outputStream.write(97);
outputStream.write(97);
outputStream.write(97);
outputStream.write(97);一次写一个
outputStream.write(97);*/
//也可以一次写多个
byte[] b = new byte[]{97,97,97};
outputStream.write(b);//一次写多个 这里的写操作每次按照写方式打开文件,都会清空原有的文件的内容,然后再从起始位置往后写
} catch (IOException e) {
e.printStackTrace();
}
}
}
Reader
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Demo11 {
public static void main(String[] args) {
try(Reader reader = new FileReader("E:/NEW/test.txt")) {
while (true){
char[] c = new char[1024];
int x = reader.read(c);//一次读多个
if(x == -1){
break;
}
/*for (int i = 0; i < x; i++) {
System.out.println(c[i]);
}*/
String s = new String(c,0,x);//如果传入的是byte也可以指定字符编码
System.out.println(s);//这样也可以读
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Writer
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Demo12 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("E:/NEW/test.txt")){
writer.write("xyz");//写操作
} catch (IOException e) {
e.printStackTrace();
}
}
}
四.文件操作案例
下面写几个文件操作的案例,再熟悉一下这些操作
1.实现查找文件并删除文件
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
//实现查找文件并删除文件
public class Demo13 {
public static void main(String[] args) {
//先输入要扫描的目录,以及要删除的文件名
Scanner input = new Scanner(System.in);
System.out.println("请输入要扫描的路径:");
String rootDirPath = input.next();
System.out.println("请输入要删除的文件名:");
String toDeleteName = input.next();
File rootDir = new File(rootDirPath);//构建文件对象
if(!rootDir.isDirectory()){
//不是一个目录
System.out.println("你输入的路径有误");
return;//可以抛一个异常
}
//遍历这个目录,寻找要删除的文件
scanDir(rootDir,toDeleteName);
}
public static void scanDir(File rootDir,String toDeleteName){
File[] files = rootDir.listFiles();
if(files == null){
//rootDir是空目录,返回
return;
}
//不是空目录就遍历此目录里面的所有内容,如果是普通文件就看是不是要删除的文件,不是文件是目录的话,就再遍历这个目录
for(File f : files){//列出rootDir里面有什么内容
if(!f.isFile()){
scanDir(f,toDeleteName);
}
if(f.isFile()){
if(f.getName().contains(toDeleteName)){
//不要求名字完全一样,只要文件名中包含了关键字就可以删除
//进行删除操作
deleteFile(f);
}
}
}
}
public static void deleteFile(File f){
try {
System.out.println(f.getCanonicalPath() + "确认要删除文件吗(Y/N)");
Scanner input = new Scanner(System.in);
String chose = input.next();
if(chose.equals("Y") || chose.equals("y")){
f.delete();//进行删除
System.out.println("文件删除成功");
}else{
System.out.println("文件放弃删除");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.文件复制
import java.io.*;
import java.util.Scanner;
//文件复制,需要让用户支出两个文件路径,一个是原路径(被复制的文件),另一个是目标路径(复制后生成的文件)
public class Demo14 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入要拷贝的源路径:");
String str = input.next();
System.out.println("请输入要拷贝的目标路径:");
String sub = input.next();
File file = new File(str);
if(!file.isFile()){//需要判断是不是正确的路径,目标路径不需要判断,因为没有的话,是会自己创建的
System.out.println("不是一个正确的源路径");
return;
}
//将源文件里面的内容读出来,然后再用写操作写到目标文件中去
try(InputStream inputStream = new FileInputStream(str)) {
try(OutputStream outputStream = new FileOutputStream(sub)) {
byte[] b = new byte[1024];
while(true){
int x = inputStream.read(b);
if(x == -1){
break;
}
outputStream.write(b,0,x);//需要指定长度,不能把所有的b都放进去
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.进行文件内容的查找
import java.io.*;
import java.util.Scanner;
//进行文件内容的查找,扫描指定目录,找到名称或内容中包含指定字符的所有普通文件(不包含目录)
public class Demo15 {
public static void main(String[] args) throws IOException {
Scanner input = new Scanner(System.in);
System.out.println("请输入要扫描的文件路径:");
String rootDirPath = input.next();
System.out.println("请输入要查询的关键字:");
String word = input.next();
File rootDir = new File(rootDirPath);
if(!rootDir.isDirectory()){
System.out.println("输入的路径非法!");
return;
}
sDir(rootDir,word);
}
private static void sDir(File rootDir, String word) throws IOException {
File[] files = rootDir.listFiles();
if(files == null){
return;
}
for (File f:files) {
if(f.isDirectory()){
sDir(f,word);
}
if(f.isFile()){
//针对文件进行查找
if(containsWord(f,word)){
System.out.println(f.getCanonicalPath());
}
}
}
}
private static boolean containsWord(File f, String word) {
StringBuilder stringBuilder = new StringBuilder();
//把f中的内容都读出来,放到StringBuilder中,看是否包含关键字
try(Reader reader = new FileReader(f)){
char[] c = new char[1024];
while(true){
int x = reader.read(c);
if(x == -1){
break;
}
stringBuilder.append(c,0,x);
}
} catch (IOException e) {
e.printStackTrace();
}
//indexOf返回的是子串的下标,如果word存在,那么下标一定不是-1,
return stringBuilder.indexOf(word) != -1;
}
}
这里主要还是要多一下熟悉这些文件操作的类方法!!