JavaEE-文件操作与IO

目录

1,两种路径

二,两种文件

三,文件的操作/File类:

1)文件系统操作文件系统操作)

File类

2)文件内容操作(读文件,写文件)文件内容操作(读文件,写文件))

(1)打开文件

(2)关闭文件

(3)读文件/InputStream

(4)写文件/OutputStream

(5)读文件/reader

(6)写文件/writer

(7)Scanner

四,练习:


1,两种路径

1)绝对路径:

绝对路径:从根开始,⼀直到达的结点的路径描述

2)相对路径:

相对路径:要制定好基准目录,'.'表示当前目录,'..'表示目录的上一级目录

绝对路径只适用于你自己的的电脑,代码一旦换到别人的电脑上,如果依赖于绝对路径,很有可能出现在你电脑上能跑,但是在别人的电脑上跑不了

二,两种文件

1)文本文件:

文本文件:当前文件里面存储的都是合法的字符,虽然叫文本文件,但实际上还是二进制文件,只不过这些二进制数据都可以根据字符集查到对应的文字。

2)二进制文件

二进制文件:如果在字符集中不可查,就是二进制文件。

三,文件的操作/File类:

1)文件系统操作

(创建文件,删除文件,创建目录,重命名文件,判定文件存在)

文件系统操作,Java提供了一个File类,这个对象会使用路径进行初始化,从而表示一个具体的文件,基于这个对象进行后续操作

代码中写的相对路径的基准路径是啥,取决于,程序的运行方式

1,直接在IDEA运行,此时,基准路径就是改项目所在的目录

2,在命令行中,通过Java命令来运行,此时,基准路径就是Java命令所处的目录

3,某个程序,可能被其他程序调用,进程1通过创建子进程的方法运行进程2,进程2的基准路径就和进程1相同

4,代码执行中,还可以通过一下API修改路径,改成我们指定的路径

File类

如果初始化File对象的参数是"相对路径",那么此IDEA运行程序,基准路径就是idea打开这个项目所在的路径

创建文件很有可能会抛出异常,原因:

1)硬盘空间不够

2)没有权限

示例:

get系列:

复制代码
File file=new File("../发言稿.docx");
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());

创建/销毁文件:

复制代码
File file=new File("./演讲稿.docx");
boolean ok=file.createNewFile();
System.out.println(ok);
System.out.print(file.exists()+" ");
System.out.print(file.isFile()+" ");
System.out.println(file.isDirectory());
System.out.println(file.delete());
System.out.print(file.exists()+" ");
System.out.print(file.isFile()+" ");
System.out.println(file.isDirectory());

File file=new File("./演讲稿.docx");
file.deleteOnExit();//进程结束之后才去删除,存在的意义就是构建临时文件

列举目录下的文件:

复制代码
File file=new File(".");
System.out.println(Arrays.toString(file.list()));
直接使用List/ListFile只能看到当前目录的内容,如果想要看到某个目录下所有的目录和文件就需要进行递归来完成
private static void scan(File currentDir){
    if (!currentDir.isDirectory()){
        return;
    }
    File[] files= currentDir.listFiles();
    if (files==null||files.length==0){
        return;
    }
    for (File f:files) {
        if (f.isFile()){
            System.out.println(f.getAbsolutePath());
        }else {
            scan(f);
        }
    }
}
public static void main(String[] args) {
    File f=new File("./");
    scan(f);
}

创建目录:

复制代码
File file=new File("./abc/def/gij");
System.out.println(file.mkdir());

2)文件内容操作(读文件,写文件)

读文件,写文件.都是操作系统提供的API.Java也进行了封装

Java实现的IO流的类有很多种,主要分成两大类:

1)字节流(二进制):读写数据的基本单位是字节=>InputStream/OutputStream

2)字符流(文本):读写数据的基本单位是字节(会自动查询码表,将二进制数据转换成字符)=>Reader/Writer

(1)打开文件

(2)关闭文件

打开文件,实际上在该进程的文件描述符表中,创建一个新的表项.文件描述表,可以认为是一个数组,数组的每个元素就是一个struct File对象(Linux内核)每个结构体就描述了对应操作的文件信息,数组的下标就称之为"文件描述符".每次打开一个文件,就相当于在数组上占用了一个位置,而在系统内核中,文件描述符表数组,是固定长度的.除非主动调用close关闭文件,此时才可以释放空间,如果代码里一直开着,不去关闭,就会使这里的一个资源越来越多,直到数组满了,再打开文件,就会打开文件失败.这就称之为"文件资源泄露"

复制代码
InputStream inputStream=null;
try {
     inputStream=new FileInputStream("./text.txt");
    //此处隐含了一个操作=>打开文件操作
}catch (IOException e){
   e.printStackTrace();
} finally {
    try {
        inputStream.close();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

但是以上写法比较麻烦,我们可以写成以下这种方法:

try with resources方法:

复制代码
try(InputStream inputStream=new FileInputStream("./text.txt")) {

}catch (IOException e){
    e.printStackTrace();
}

在try的括号里面可以创建多个资源,try的{}结束后,最终会自动执行这里的close,但是try里面的资源必须是实现Closeable接口的

(3)读文件/InputStream

从硬盘上读取到内存上

返回值之所以不用byte[0->255],当前返回值是要能表示-1这种特殊情况. (文件读取完毕,value就会返回-1).

复制代码
public static void main(String[] args) {
    try(InputStream inputStream=new FileInputStream("./text.txt")) {
        while (true){
            int b=inputStream.read();
            if (b==-1){
               break;
            }
            System.out.printf("0x%x\n",b);//十六进制打印
        }
    }catch (IOException e){
        e.printStackTrace();
    }
}

这个操作将从硬盘中读取到的内容,填充到内存的字节数组中(一次IO,效率提高不少)

复制代码
try(InputStream inputStream=new FileInputStream("./text.txt")) {
    while (true) {
        byte[] buffer = new byte[1024];
        int n = inputStream.read(buffer);//这时,buffer相等于"输出型参数"
        //n表示,实际读到的字节数
        if (n == -1) {
            break;
        }
        for (int i = 0; i < n; i++) {
            System.out.printf("0x%x\n", buffer[i]);
        }
    }
}catch (IOException e){
    e.printStackTrace();
}

第三个版本,与第二个版本类似,只不过不是使用整个数组,而是在数组[off,off+len]范围区间

(4)写文件/OutputStream

复制代码
try (OutputStream outputStream=new FileOutputStream("./text.txt")){
    outputStream.write(0xe4);
    outputStream.write(0xbd);
    outputStream.write(0xa0);
    outputStream.write(0xe5);
    outputStream.write(0xa5);
    outputStream.write(0xbd);
} catch (IOException e) {
    e.printStackTrace();
}

此处的写操作会将之前的文件清空(只要OutputStream打开文件,文件里面的数据就没有了),想要保持原内容不变,还有一个"追加写",可以在初始化OutputStream的对象的时候增加一个参数

复制代码
try (OutputStream outputStream=new FileOutputStream("./text.txt",true)){
    byte[] buffer=new byte[]{(byte)0xe4,(byte)0xbd,(byte)0xa0,(byte)0xe5,(byte)0xa5,(byte)0xbd };
    outputStream.write(buffer);
} catch (IOException e) {
    e.printStackTrace();
}

(5)读文件/reader

复制代码
try (Reader reader=new FileReader("./text.txt")){
    while (true) {
        int c = reader.read();
        if (c == -1) {
            return;
        }
        char ch = (char) c;
        System.out.println(ch);
    }
}catch (IOException e){
    e.printStackTrace();
}

最初在文档里面的的时候使用的是utf8编码,每个汉字是三个字节,但是在Java中每个汉字是两个字节,这是因为这里不再使用utf8而是使用Unicode,在Unicode中一个字符就是两个字节(在使用字符流读数据的时候,Java标准库内部就自动针对数据进行了转码操作)

复制代码
try (Reader reader=new FileReader("./text.txt")){
    while (true) {
        char[]buffer=new char[1024];
        int n=reader.read(buffer);
        System.out.println(n);
        for (int i = 0; i <n ; i++) {
            System.out.println(buffer[i]);
        }
    }
}catch (IOException e){
    e.printStackTrace();
}

(6)写文件/writer

复制代码
try (Writer writer=new FileWriter("./text.txt",true)){
    writer.write("hello world");
}catch (IOException e){
    e.printStackTrace();
}

(7)Scanner

复制代码
try (InputStream inputStream=new FileInputStream("./text.txt")){
    Scanner scanner=new Scanner(inputStream);
    while (scanner.hasNextInt()){
        System.out.println(scanner.nextInt());
    }
}catch (IOException e){
    e.printStackTrace();
}

四,练习:

1,扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否 要删除该⽂件

复制代码
 System.out.println("请输入要查找的路径");
    Scanner scanner=new Scanner(System.in);
    File rootFile=new File(scanner.nextLine());
    if (!rootFile.isDirectory()){
        System.out.println("查找的路径不存在");
        return;
    }
    System.out.println("输入要删除的文件名称");
    String key=scanner.nextLine();
    scan(rootFile,key);
}

private static void scan(File rootFile, String key) {
    if (!rootFile.isDirectory()){
        return;
    }
    File[] files=rootFile.listFiles();
    if (files==null||files.length==0){
        return;
    }
    for (File f:files) {
        if (f.isFile()){
            deleteFile(f,key);
        }else {
            scan(f,key);
        }
    }
}

private static void deleteFile(File f,String key) {
    if (!f.getName().contains(key)){
        return;
    }
    System.out.println(f.getAbsolutePath()+" 是否删除该文件Y/N");
    Scanner scanner=new Scanner(System.in);
    String choice=scanner.nextLine();
    if (choice.equals("y")||choice.equals("Y")){
        f.delete();
    }
}

2,这里的进⾏普通⽂件的复制

复制代码
public static void main(String[] args) {
    System.out.println("输入要复制的文件的路径:");
    Scanner scanner=new Scanner(System.in);
    String path=scanner.nextLine();
    File file=new File(path);
    if (!file.isFile()){
        System.out.println("文件不存在!!");
        return;
    }
    System.out.println("请输入目标路径:");
    String destPath=scanner.nextLine();
    File file1=new File(destPath);
    if (!file1.getParentFile().isDirectory()){
        System.out.println("目标路径不存在");
        return;
    }
    try (InputStream inputStream=new FileInputStream(file);
         OutputStream outputStream=new FileOutputStream(file1)){
        while (true){
            byte[] buffer=new byte[1024];
            int n=inputStream.read(buffer);
            if (n==-1){
                break;
            }
            outputStream.write(buffer,0,n);
        }

    }catch (IOException e){
        e.printStackTrace();
    }
}

3,扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)

复制代码
public static void main(String[] args) {
    System.out.println("请输入一个路径:");
    Scanner scanner=new Scanner(System.in);
    String path=scanner.nextLine();
    File file=new File(path);
    if (!file.isDirectory()){
        return;
    }
    System.out.println("请输入要查询的关键字:");
    String key=scanner.nextLine();
    scan(file,key);
}

private static void scan(File file, String key) {
    if (!file.isDirectory()){
        return;
    }
    File[] files=file.listFiles();
    if (files==null||files.length==0){
        return;
    }
    for (File f:files) {
        if (f.isFile()){
            search(f,key);
        }else {
            scan(f,key);
        }
    }
}

private static void search(File f, String key) {
    StringBuffer stringBuffer=new StringBuffer();
    try (Reader reader=new FileReader(f)){
        char[] buffer = new char[1024];
        while (true) {
            int n = reader.read(buffer);
            if (n == -1) {
                break;
            }
            String s=new String(buffer,0,n);
            stringBuffer.append(s);
        }
        if (stringBuffer.indexOf(key)==-1){
            return;
        }
        System.out.println(f.getAbsolutePath());
    }catch (IOException e){
        e.printStackTrace();
    }
}

操作会涉及大量的IO操作,效率是非常低的,这种使用遍历的思路不适用于频繁查询,也不适用于文件繁多的场景.例如,搜索引擎,并不能通过上述方法进行查询,其中优化的核心,引入了数据结构"倒排索引",提前把所有文件的内容,关键字分析好,放到一个文件里,就这个结果,得到一份数据,每个关键词都在那些文件里面包含着

相关推荐
七夜zippoe3 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
盟接之桥3 小时前
盟接之桥说制造:引流品 × 利润品,全球电商平台高效产品组合策略(供讨论)
大数据·linux·服务器·网络·人工智能·制造
Fcy6484 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满4 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠5 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
主机哥哥5 小时前
阿里云OpenClaw部署全攻略,五种方案助你快速部署!
服务器·阿里云·负载均衡
Harvey9035 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
珠海西格电力科技6 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市
释怀不想释怀6 小时前
Linux环境变量
linux·运维·服务器
zzzsde6 小时前
【Linux】进程(4):进程优先级&&调度队列
linux·运维·服务器