文件操作和IO

文件操作

什么叫做文件

平时所谈论到的文件一般都是指存储在硬盘上的普通文件,例如txt,jpg,mp4,rar等文件都可以认为是普通文件,他们都是在硬盘上存储的.

在计算机中,文件是一个广义的概念不只是包含普通文件,还包含目录(把目录成为目录文件)

普通文件是保存在硬盘中的

image-20220405123415737

机械硬盘的基本构造:

  1. 盘片,存储数据的介质
  2. 磁头

机械硬盘一旦通上电,里面的盘头片就会高速运转,磁头就会在盘片上找到对应的数据

受限于机械硬盘的硬件结构,盘片的转速越快,.读写速度越快,因工艺的限制,盘片的转速不可能无限高,所以机械硬盘的读写速度已经有10余年停滞未前.

文件的分类

主要将文件分为两类

  1. 二进制文件
  2. 文本文件

二进制文件里面存储的是字节,所以二进制文件中的字节和字节之间没有任何的联系

文本文件中存储的是字符,文本文件的本质其实也是存放字节,但文本文件中,相邻的字节正好可以构成一个又一个的字符.

想要判断一个文件是二进制文件还是文本文件,最简单的办法就是右键打开方式,用记事本方式打开,如果打开之后是乱码.就说明是二进制文件,如果不是乱码,就说明是文本文件.

生活中,.txt .c .java都是文本文件,而.doc .ppt .exe .zip .class 都是二进制文件

目录结构

计算机中,保存管理文件,是通过操作系统的 "文件系统"这样的模块进行管理

文件系统一般是通过"树形"结构来组织磁盘上的目录和文件(N叉树)

image-20220405124927406

类似于这样的结果,量所有的文件进行管理,普通文件就是树的叶子结点,而目录文件,目录中可以包含子树,这个目录就是非叶子结点,这个树的每个节点上的子树都有N个,所以是一个N叉树

路径

在操作系统中,就通过路径这个概念来描述一个具体文件/目录的位置

路径存在两种描述风格

  1. 绝对路径:以盘符为开头 例如 D:\JAVA\java8home\bin

  2. 相对路径: 以.或者…为开头,其中 . 表示当前路径, … 表示当前路径的上一个路径 相对路径需要有一个基准路径

image-20220405125659070

此时以 D:\JAVA\java8home\bin 为基准路径,去寻找java.exe文件 , 那么此时的相对路径为 : " .\java.exe "

Java中操作文件

File概述

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

属性

修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分割符,String类型的表示
static charpathSeparator依赖于系统的路径分割符,Char类型的表示

构造方法

签名说明
File(File parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示

方法

修饰符及返回值类型方法签名说明
StringgetParent() 返回 File 对象的父目录文件路径
String getName() 返回 File 对象的纯文件名称
String getPath()返回 File 对象的文件路径
String getAbsolutePath()返回 File 对象的绝对路径
String getCanonicalPath() 返回 File 对象的修饰过的绝对路径
booleanexists() 判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile() 判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回true
booleandelete() 根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit() 根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行
String[] list() 返回 File 对象代表的目录下的所有文件名
File[] listFiles() 返回 File 对象代表的目录下的所有文件,以 File 对象
booleanmkdir()表示创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录
booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限

代码实例:

实例1
public static void main(String[] args) throws IOException {
        File f =new File("F:/happy/text.txt");
        System.out.println(f.getParent());  //返回当前File对象的父目录文件路径
        System.out.println(f.getName());    //返回当前File对象的纯文件名称
        System.out.println(f.getPath());    //返回当前File对象的路径(构造 File 的时候指定的路径)
        System.out.println(f.getAbsolutePath());//返回当前文件的绝对路径
        System.out.println(f.getCanonicalPath());//返回当前文件的绝对路径(修饰过的)
}

image-20220405134827504

执行结果

image-20220405134847456

看上面的代码,发现getAbsolutePath方法和getCanonicalPath方法好像并没有太大的区别,但其实他们的区别在于创建File对象使用相对路径

public static void main(String[] args) throws IOException {
        File f =new File("./happy/text.txt");
        System.out.println(f.getParent());  //返回当前File对象的父目录文件路径
        System.out.println(f.getName());    //返回当前File对象的纯文件名称
        System.out.println(f.getPath());    //返回当前File对象的路径(构造 File 的时候指定的路径)
        System.out.println(f.getAbsolutePath());//返回当前文件的绝对路径
        System.out.println(f.getCanonicalPath());//返回当前文件的绝对路径(修饰过的)
    }

image-20220405135249948

image-20220405135300992

可以看到,在使用getAbsolutePath方法时,最后返回的是 F:\Java代码\.\happy\text.txt 返回的地址是在基准地址的基础上,把相对路径拼接上来了,此时的 . 仍然是当前目录,也就是Java代码这一级目录,完全可以将 .去掉

相对路路径的基准路径

  1. 如果通过命令行的方式,此时执行命令行所在的路径就是基准路径
  2. 通过IDEA的方式来运行程序,此时基准路径就是当前java项目所在的路径
  3. 如果将java代码打包成war包,放在tomcat上运行,这种情况,基准路径就是tamcat1bin路径

路径之间的分隔符

在绝大多数的操作系统中,路径分隔符都是"/“斜杠,但对于windows来说,使用”\"反斜杠,在如今的windows操作系统中,/ 和\都可以正常使用

实例2
public static void main(String[] args) {
    File f = new File("F:/happy/text.txt");
    System.out.println(f.exists()); //判断一个文件是否存在
    System.out.println(f.isDirectory());//判断一个文件是不是目录文件
    System.out.println(f.isFile()); //判断一个文件是不是普通文件
}

image-20220405141125621

此三个方法来判断文件是否存在,文件是目录文件还是一个普通文件

实例3
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(f.exists()); //判断文件是否存在
}

image-20220405141518441

一开始的相对路径下没有text.txt文件,调用createNewFile方法后,在这个java路径下创建了一个新的文件

image-20220405141642811

实例4
public static void main(String[] args) {
    File f = new File("./text.txt");
    System.out.println(f.exists());
    System.out.println("删除文件!");
    System.out.println(f.delete());
    System.out.println("删除完成!");
    System.out.println(f.exists());
}

image-20220405142204391

实例5
public static void main(String[] args) {
    File f = new File("./zzz");
    f.mkdir(); //创建出File对象代表的路径
    System.out.println(f.isDirectory());
}

image-20220405142829281

实例6

public static void main(String[] args) {
    File f = new File(".aaa/bbb/ccc");
    f.mkdirs();
    System.out.println(f.isDirectory());
}

image-20220405143112111

实例5和实例6创建的全部都是目录文件,不可以创建普通文件

案例6
public static void main(String[] args) {
    File f = new File("./");
    System.out.println(Arrays.toString(f.list()));
    System.out.println(Arrays.toString(f.listFiles()));
}

image-20220405143638153

list 方法和listFiles方法的区别就是,list方法的返回类型是String类型,而listFiles方法的返回值是一个file数组类型

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

对于一个文件的操作通常有以下几个操作

  1. 打开文件
  2. 读文件
  3. 写文件
  4. 关闭文件

针对文件内容的读写,Java标准库提供了一组类,按照文件的类容,分为两个系列

  1. 字节流对象,针对二进制文件,以字节为单位进行读写
    1. 读 :InputStream(抽象类) 对应的子类 FileInputStream
    2. 写:OutputStream(抽象类) 对应的子类 FileOutputStream
  2. 字符流对象,针对文本文件,以字符为单位进行读写
    1. Reader 子类 FileReader
    2. Writer 子类FileWriter

上述几个类的使用方法其实是一模一样的

什么是流?

我们将流比作水流

例如,想通过一个水龙头 ,接100ml的水,可以一次接10ml,分10次接完,也可以一次接20ml,分5次接完,还可以一次接100ml,一次接完

我们想通过流对象,读取100个字节,可以一次读10个字节,10次读完,可以一次读20个字节,5次读完,也可以一次读100个字节,一次读完

InputStream概述

方法

修饰符及返回值类型方法签名说明
intread()读取一个字节的数据,返回 -1 代表已经完全读完了
intread (byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表以及读完了
intread(byte[] b,int off, int len)最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流

说明

InputStream 只是一个抽象类,要使用还需要具体的实现类。关于InputStream的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用 FileInputStream

read提供的三个版本

无参数版本:一次读一个字节,返回值就是读到的这个字节

一个参数版本:一次读取若干个参数,读到的结果放到参数中指定的数组中,返回值就是读到的字节数

三个参数版本:一次读取若干个参数,读到的结果放到参数中指定的数组中,返回值就是读到的字节数.不是从数组的起始位位置放置,而是从中间位置(off下标位置开始),len是最多能放多少个元素

关于字节的应该是byte,但是这个返回的却是int的解释:

在源码中,返回-1表示读到了文本末尾,但是byte的范围只有 -128 到127,能取的数太少了,所以用int

FileInputStream 概述

构造方法

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

最普通版本:

    public static void main(String[] args) {
        //1. 创建对象,同时也是打开文件
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream("f:/happy/text.txt");
            // 2 尝试一个一个字节的读,将所有的文件都读完
            while(true) {
                int b = inputStream.read();
                if(b == -1) {
                    //读到文件末尾
                    break;
                }
                System.out.println(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //3. 读完以后再关闭文件
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

image-20220405151046257

虽然上述代码可以将文件中的数据以字节的形式读取.但是代码太过冗余,后期维护工作量大,所以可以进行优化

优化版本:

try (InputStream inputStream = new FileInputStream("f:/happy/text.txt")) {
    while (true) {
        int b = inputStream.read();
        if (b == -1) {
            break;
        }
        System.out.println(b);
    }
} catch (IOException e) {
    e.printStackTrace();
}

try(){

}catch{}

第一行的()里面是对资源的申请,如果{}中的代码出现异常,()中的资源会被关闭,这些可关闭的资源必须实现Closeable接口。

除了一个一个的将数据读取,还可以将读取到的数据放到一个数组中

try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
    // 一次读取若干个字节.
     while (true) {
        byte[] buffer = new byte[1024];
        int len = inputStream.read(buffer);
        if (len == -1) {
        // 如果返回 -1 说明读取完毕了
            break;
        }
        for (int i = 0; i < len; i++) {
            System.out.println(buffer[i]);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

OutputStream概述

方法

修饰符及返回值类型方法签名说明
voidwrite(int b)写入要给字节的数据
voidwrite(byte[] b)将 b 这个字符数组中的数据全部写入 os 中
intwrite(byte[] b,int off,int len)将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
voidclose()关闭字节流

每次按照写方法打开文件,会清空原有文件的内容,清空旧的内容,再从起始位置往后写

同时也有一个追加写的刘对象,打开之后不清空,从文件末尾继续写

public static void main(String[] args) {
    try(OutputStream outputStream = new FileOutputStream("f:/happy/text.txt")) {
        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);
        outputStream.write(100);
    }catch (IOException e) {
        e.printStackTrace();
    }
}

image-20220405153758072

不清空版本

try(OutputStream outputStream = new FileOutputStream("f:/happy/text.txt",true)) {
    outputStream.write(97);
    outputStream.write(98);
    outputStream.write(99);
    outputStream.write(100);
}catch (IOException e) {
    e.printStackTrace();
}

image-20220405154217102

一个一个的写太过麻烦,我们可以创建一个byte数组,然后进行写

try (OutputStream outputStream = new FileOutputStream("f:/happy/text.txt")){
     byte[] b = {97,98,99};
     outputStream.write(b);
}catch (IOException e) {
     e.printStackTrace();
}

image-20220405154526489

Reader

这是针对字符进行读操作

try(Reader reader = new FileReader("f:/happy/text.txt")) {
    while (true){
        char[] buffer = new char[1024];
        int len = reader.read(buffer);
        if(len == -1) {
            break;
        }
        String s = new String(buffer, 0, len);
        System.out.println(s);
    }
}catch (IOException e) {
    e.printStackTrace();
}

image-20220405155753868

Writer

public static void main(String[] args) {
    try (Writer writer = new FileWriter("f:/happy/text.txt")) {
        writer.write("xyz");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

image-20220405155924679

Reader 和Writer接口和InputStream 以及OutputStream接口的方法其实都是差不多的.

利用 Scanner 进行字符读取

构造方法说明
Scanner(InputStream is,String charst)使用 charset 字符集进行 is 的扫描读取
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
           try (Scanner scanner = new Scanner(is, "UTF-8")) {
               while (scanner.hasNext()) {
                   String s = scanner.next();
                   System.out.print(s);
               }
           }
       }
   }
}

利用PrintWriter找到我们熟悉的方法

上述,我们其实已经完成输出工作,但总是有所不方便,我们接来下将 OutputStream 处理下,使用

PrintWriter 类来完成输出,因为

PrintWriter 类中提供了我们熟悉的 print/println/printf 方法

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            try (OutputStreamWriter osWriter = new OutputStreamWriter(os, "UTF-8")) {
                try (PrintWriter writer = new PrintWriter(osWriter)) {
                    writer.println("我是第一行");
                    writer.print("我的第二行\r\n");
                    writer.printf("%d: 我的第三行\r\n", 1 + 1);
                    writer.flush();
               }
           }
       }
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值