【JavaEE初阶】文件操作——IO

摄影分享~
【JavaEE初阶】文件操作——IO_第1张图片

文章目录

  • 文件
    • 文件路径(Path)
  • 文件的类型
  • Java中操作文件
    • File概述
  • 文件内容的读写——数据流
    • 字节流
    • InputStream概述
    • OutputStream 概述
    • 字符流
    • FileInputStream 概述
    • 利用 Scanner 进行字符读取
  • 实例练习

文件

文件:File这个概念,在计算机里也是一次多用:
狭义的文件:指的是硬盘上的文件和目录。针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。
广义的文件:泛指计算机中的很多的软硬件资源.

同时,随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢,一种合乎自然的想法出现了,就是按照层级结构进行组织 —— 也就是我们数据结构中学习过的树形结构。这样,一种专门用来存放管理信息的特殊文件诞生了,也就是我们平时所谓文件夹(folder)或者目录(directory)的概念。

文件路径(Path)

每个文件,在硬盘上都有一个具体的路径。
【JavaEE初阶】文件操作——IO_第2张图片
例如:上述照片的路径就是:E:/rocket.jpg
表示一个文件的具体位置路径,就可以使用 / 来分割不同的目录级别。

【JavaEE初阶】文件操作——IO_第3张图片
就可以表示为:E:\tmp\111\aaa

在Windows中/\都可以来做分隔符。但是在平常的代码中更建议使用/,因为/不存在字符转义问题。

在路径这里,有两种表示路径的风格。

  1. 绝对路径,以c: d: 盘符开头的路径
  2. 相对路径,以当前所在的目录为基准,以.或者..开头(有时可以省略),找到指定的路径

当前所在目录:称为工作目录,每个程序运行的时候,都有一个工作目录。

【JavaEE初阶】文件操作——IO_第4张图片
定位到111这个目录,是E:/tmp
如果工作目录不同,定位到同一个文件,相对路径写法是不同的:
例如:定位到111
如果工作目录是E:/ 相对路径写作:./tmp/111
如果工作目录是E:/tmp 相对路径写作:./111
如果工作目录是E:/tmp/222 相对路径写作:../111
如果工作目录是E:/tmp/222/bbb 相对路径写作:../../111

..表示上一级路径

文件的类型

文件的类型有很多种,比如:word,exe,图片,视频,pdf,源代码,动态库等。这些文件可以归纳到两类中:

  1. 文本文件(存的是文本,字符串)
    字符串,是由字符构成的,每个字符,都是通过一个数字来表示的。这个文本文件里存的数据,一定是合法的字符,都是指定字符编码的码表之内的数据。
  2. 二进制文件(存的是二进制,不一定是字符串)
    没有任何限制

如何判断一个文件是二进制还是文本文件?
使用记事本打开,如果乱码就是二进制文件;如果没有乱码,就是文本。
【JavaEE初阶】文件操作——IO_第5张图片

Java中操作文件

主要分为两类:

  1. 针对文件系统操作。(文件的创建,删除,重命名)
  2. 针对文件内容操作。(文件的读和写)

Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不
代表真实存在该文件。

File概述

常见属性

修饰符及类型 属性 说明
static String pathSeparator 依赖于系统的路径分隔符,String 类型的表示
static char pathSeparator 依赖于系统的路径分隔符,char 类型的表示

pathSeparator(File中的一个静态变量)就是/或者\跟着系统走。

构造方法

签名 说明
File(File parent, Stringchild) 根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname) 根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent, Stringchild) 根据父目录 + 孩子文件路径,创建一个新的 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 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行
String[] list() 返回 File 对象代表的目录下的所有文件名
File[] listFiles() 返回 File 对象代表的目录下的所有文件,以 File 对象表示
boolean mkdir() 创建 File 对象代表的目录
boolean mkdirs() 创建 File 对象代表的目录,如果必要,会创建中间目录
boolean renameTo(Filedest) 进行文件改名,也可以视为我们平时的剪切、粘贴操作
boolean canRead() 判断用户是否对文件有可读权限
boolean canWrite() 判断用户是否对文件有可写权限

方法演示:
【JavaEE初阶】文件操作——IO_第6张图片
注意:

File file = new File("./test.txt");

中不要求E:/这里真的有test.txt
如果想要可以自己手动创建:
【JavaEE初阶】文件操作——IO_第7张图片
【JavaEE初阶】文件操作——IO_第8张图片
【JavaEE初阶】文件操作——IO_第9张图片

throws IOException

这个异常在IO中是特别容易出现的异常。

deleteOnFile()程序退出的时候,自动删除。(打开一个word文档,就会在同级目录下生成出一个临时文件,关闭word,这个文件就没有了)
【JavaEE初阶】文件操作——IO_第10张图片
这个临时文件,相当于保存了当前实时编辑的内容,防止编辑了很多东西之后突然停电,导致数据丢失。这种临时数据就使用deleteOnExit的方式来删除。

【JavaEE初阶】文件操作——IO_第11张图片
【JavaEE初阶】文件操作——IO_第12张图片

文件内容的读写——数据流

什么叫做流呢?

举个:
水龙头,通过这个水龙头可以接水。
比如你要接100ml水,你可以一次接100ml,一次接完
也可以一次接50ml,分两次接
还可以一次接10ml,分10次接

这是水流~~

同理,从文件中读100个字节,就可以:
一次性读100字节,一次读完。
一次性读50字节,两次读完。
一次读10字节,十次读完。

因此就把读写文件的相关对象,称为“流对象”

Java标准库的流对象,从类型上分为两大类:

  1. 字节流:操作二进制数据的
    InputStream
    OutoutStream
    FileInputSrteam
    FileOutputStream
  2. 字符流:操作文本数据的
    Reader
    Writer
    FileReader
    FileWriter

这些类的使用方法是非常固定的,核心就是四个操作:

  1. 打开文件
  2. 关闭文件
  3. 读文件(read)=>针对InputStream/Reader
  4. 写文件(writer)=>针对OutputStream/Writer

注意InputStream OutoutStream Reader Writer都是抽象类,不能直接new

字节流

InputStream概述

【JavaEE初阶】文件操作——IO_第13张图片
可以看到read的三个版本:

方法:

修饰符及返回值类型 方法签名 说明
int read() 一次读取一个字节,返回 -1 代表已经完全读完了
int read(byte[] b) 把读到的内容填充到参数的这个字节数组中(此处的参数是“输出型参数”)返回值是实际读取的字节数
int read(byte[] b,int off, int len) 最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了
void close() 关闭字节流

【JavaEE初阶】文件操作——IO_第14张图片

注意FileNotFoundExpectionIOExpection的一个子类。所以抛异常的时候只需要抛IOExpection就够了。

read();无参数版本详解:

【JavaEE初阶】文件操作——IO_第15张图片

【JavaEE初阶】文件操作——IO_第16张图片
可以看到打印出来是abcd的ascii值
如果文本中是中文“你好。”那么打印出来则为:
【JavaEE初阶】文件操作——IO_第17张图片
这里使用的utf8,utf8码表对应的16进制则为以上值。(utf8中一个汉字对应三个字节)

read(byte[])版本详解:
【JavaEE初阶】文件操作——IO_第18张图片
此版本中需要调用者提前准备好一个数组buffer(缓冲区)用来提高IO操作的效率。

int len = inputStream.read(buffer);

中传参操作,相当于是把刚才准备好的数组,交给read方法,让read方法内部针对这个数组进行填写。(此处参数相当于输出型参数)

注意read的行为和返回值。read会尽可能的把参数传进来的数组给填满。上面这里给出的数组长度时1024,read就会尽可能的读取1024个字节,填到数组中。但实际上,文件剩余长度是有限的,如果剩余长度超过1024,此时1024个字节都会被填满,返回值就是1024了。如果当前剩余的长度不足1024,此时有多少就填多少。read方法就会返回当前实际读取的长度。
读完了,一个字节都没读到,则返回-1.

【JavaEE初阶】文件操作——IO_第19张图片
使用了InputStream来读文件,可以使用OutStream来写文件。

OutputStream 概述

修饰符及返回值类型 方法签名 说明
void write(int b) 写入要给字节的数据
void write(byte[]b) 将 b 这个字符数组中的数据全部写入 os 中
int write(byte[]b, int off,int len) 将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
void close() 关闭字节流
void flush() 重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。

详解write(int b)
【JavaEE初阶】文件操作——IO_第20张图片
【JavaEE初阶】文件操作——IO_第21张图片
注意:在abcd之前记事本中的内容是“你好”。对于OutputStream来说,默认情况下,打开一个文件,会先清空文件原有的内容。
如果不想清空,流对象还提供了一个“追加写”对象。通过这个可以实现不清空文件,把新内容追加到写后面。
注意:inputoutput的方向:是以CPU为中心,来看待这个方向的。
【JavaEE初阶】文件操作——IO_第22张图片

以CPU为中心:
流向CPU的方向,就是输入。所以就把数据从硬盘到内存这个过程称为读(input)
流出CPU的方向,就是输出。所以就把数据从内存到硬盘这个过程称为写(output)

详解close()

outputStream.close();

这里的close操作,含义是关闭文件。
进程->在内核中,使用PCB这样的数据来表示进程。
一个线程对应一个PCB。一个进程可以对应多个PCB。
PCB中有一个重要的属性:文件描述符表(相当于一个数组,记录了该进程打开了哪些文件)
一个进程里有多个线程多个PCB,这些PCB公用同一个文件描述符表。

【JavaEE初阶】文件操作——IO_第23张图片
每次打开文件操作,就会在文件描述符表中,申请一个位置。把这个信息放进去。每次关闭文件,也就会把这和文件描述符表对应的表项给释放。

那么,如果我们忘记写这个close();会怎么办呢?

如果没写close,对应的表项,没有及时释放。虽然Java有GC(垃圾回收机制),GC操作会在回收这个outputStream对象的时候去完成这个释放操作,但是这个GC回收器不一定及时。所以,如果不手动释放,意味着文件描述符表可能很快就被占满了。如果占满了之后,后面再次打开文件,就会打开失败。文件描述符表最大长度,不同系统上不太一样,基本是(几百到几千)

我们应该如何保证这个close会被执行到呢?

【JavaEE初阶】文件操作——IO_第24张图片
上述写法虽然没有显式的写close,实际上是会执行的。只要try语句块执行完毕,就可以自动执行到close

这个语法在Java中称为 try with resources
当然,要使用这个语法必须实现Closeable接口的类,才可以放到try的()中被自动关闭。

close操作,会触发缓冲区的刷新。(刷新操作,就是把缓冲里的内容写到硬盘里)

字符流

用法和字节流类似。

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class IODemo7 {
    // 字符流的操作
    public static void main(String[] args) {
        try (Reader reader = new FileReader("e:/test.txt")) {
            while (true) {
                int ch = reader.read();
                if (ch == -1) {
                    break;
                }
                System.out.println("" + (char)ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【JavaEE初阶】文件操作——IO_第25张图片

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class IODemo9 {
    public static void main(String[] args) {
        try (Writer writer = new FileWriter("e:/test.txt")) {
            writer.write("hello world");
            // 手动刷新缓冲区
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【JavaEE初阶】文件操作——IO_第26张图片

FileInputStream 概述

构造方法:

签名 说明
FileInputStream(File file) 利用 File 构造文件输入流
FileInputStream(String name) 利用文件路径构造文件输入流

【JavaEE初阶】文件操作——IO_第27张图片

利用 Scanner 进行字符读取

构造方法 说明
Scanner(InputStream is, String charset) 使用 charset 字符集进行 is 的扫描读取
 Scanner scanner = new Scanner(System.in);

System.in就是一个输入流对象。

实例练习

  1. 扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
import java.io.File;
import java.util.Scanner;

public class IODemo11 {
    private static Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) {
        //让用户输入一个指定搜索的目录
        System.out.println("请输入一个指定搜索的路径:");
        String basePath = scanner.next();

        //针对用户输入进行简单判断
        File root = new File(basePath);
        if(!root.isDirectory()){
            //路径不存在,或者只是一个普通文件,就无法进行搜索
            System.out.println("输入的目录有误!");
            return;
        }

        //再让用户输入一个要删除的文件名
        System.out.println("请输入要删除的文件名:");
        String nameToDelete = scanner.next();

        //针对指定的路径进行扫描,递归操作
        //先从跟目录出发(root)
        //先判定一下这个目录中是否包含我们需要删除的文件,如果是就删除
        //否则就跳过,下一个
        //如果当前这里包含了一些目录,再针对子目录进行递归

        scanDir(root,nameToDelete);
    }

    private static void scanDir(File root, String nameToDelete) {
        System.out.println("[scanDir]"+root.getAbsolutePath());//查看递归的查找路径
        //1.先列出root下的文件和目录
        File[] files = root.listFiles();
        if(files == null){
            //当前root目录下为空
            //结束递归
            return;
        }
        //2.遍历当前列出的结果
        for (File f: files) {
            if(f.isDirectory()){
                //如果是目录就进一步递归
                scanDir(f,nameToDelete);
            }else{
                //如果是普通文件,则判定是否要删除?
                if(f.getName().contains(nameToDelete)){
                    System.out.println("是否确认删除"+f.getAbsolutePath());
                    String choic = scanner.next();
                    if(choic.equals("yes")||choic.equals("YES")){
                        f.delete();
                        System.out.println("删除成功!");
                    }else{
                        System.out.println("删除取消!");
                    }
                }
            }
        }
    }
}

【JavaEE初阶】文件操作——IO_第28张图片

【JavaEE初阶】文件操作——IO_第29张图片

  1. 进行普通文件的复制
    把一个文件拷贝成另一个文件
import java.io.*;
import java.util.Scanner;

public class IODemo8 {
    public static void main(String[] args) {
        //输入两个路径
        //源 和 目标
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要拷贝那个文件:");
        String srcPath = scanner.next();
        System.out.println("请输入要被拷贝到哪里:");
        String destPath = scanner.next();

        File srcFile = new File(srcPath);
        if(!srcFile.isFile()){
            //如果源不是一个文件/目录不存在
            //就什么也不做
            System.out.println("您输入的源路径有误!");
            return;
        }
        File destFile = new File(destPath);
        if(destFile.isFile()){
            //如果目标存在,也不能拷贝
            System.out.println("您输入的目标路径有误!");
            return;
        }
        try(InputStream inputStream = new FileInputStream(srcFile);
            OutputStream outputStream = new FileOutputStream(destFile)){
            while(true){
                int b = inputStream.read();
                if(b == -1){
                    break;
                }
                outputStream.write(b);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

【JavaEE初阶】文件操作——IO_第30张图片

【JavaEE初阶】文件操作——IO_第31张图片
【JavaEE初阶】文件操作——IO_第32张图片

你可能感兴趣的:(JavaEE初阶,java-ee,java,数据结构)