自学内容网 自学内容网

Java 文件IO

一、什么是文件IO

文件是一个广义的概念,操作系统将很多资源都抽象成文件,这篇文章讲解文件特指硬盘上的文件

在硬盘上存在很多文件和目录,它们以一种N叉树的结构存储

注意:文件夹也是一种文件,它是一种目录文件

二、路径

为了能定位到某个具体文件,需要引入路径,路径就是从根节点为起点,经过一些目录最终到达目标文件的"集合";路径分为绝对路径相对路径

1)绝对路径:

以盘符为开头的路径称为绝对路径,如:D:/program/qq/Bin/qq.exe

2)相对路径:

相对路径的起点可以是任意路径

这个是qq.exe的绝对路径:D:/program/qq/Bin/qq.exe

假如当前的基准是D:/program/qq/Bin,那相对路径可以写成:. /qq.exe. 表示当前位置

当前基准是D:/program/qq,相对路径可以写成:./Bin/qq.exe

当前基准是D:/program/qq/Bin/plugins,相对路径可以写成:. . /qq.exe(..表示返回上一级目录)

三、文件的分类

文件的分类有很多种,这里讲解一种和编写代码密切相关的:文本文件 vs 二进制文件

将文件从记事本打开:

左侧:文本文件;右侧:乱码→二进制文件

文本文件是给人看的,二进制文件是给程序看的

四、使用Java操作文件

1)针对文件系统操作,如:创建文件、删除文件、重命名文件、创建目录等

2)针对文件内容操作,如:读文件、写文件

4.1 针对文件系统

Java标准库提供了 File 类来表示一个文件实例,File 提供了一些构造方法和方法

构造方法描述

File (File parent,String child)

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

方法描述

String  getParent()    

返回File对象的父目录文件路径
String  getName()    返回FIle对象的纯文件名称
String  getPath()    返回File对象的文件路径
String  getAbsolutePath()    返回File对象的绝对路径
String  getCanonicalPath()    返回File对象的修饰过的绝对路径

方法演示:

File file = new File("./test.txt"); //.代表当前项目的路径

System.out.println("file.getParent(): "+ file.getParent());

System.out.println("file.getName(): "+ file.getName());

System.out.println("file.getPath() :"+ file.getPath());

System.out.println("file.getAbsolutePath(): "+ file.getAbsolutePath());

System.out.println("file.getCanonicalPath(): "+ file.getCanonicalPath());

结果演示:


方法描述
boolean  exists()    判断File对象描述的⽂件是否真实存在
boolean  isDirectory()  判断File对象代表的⽂件是否是⼀个⽬录
boolean  isFile()  判断File对象代表的⽂件是否是⼀个普通⽂件
boolean  createNewFile()    根据File对象,⾃动创建⼀个空⽂件。成功创建后返回true

方法演示:

File file = new File("./test.txt");
// 此时还没有创建文件, file只是一个File实例

System.out.println(file.exists()); //false

System.out.println(file.isFile()); //false

System.out.println(file.isDirectory()); //false
File file = new File("./test.txt");
// 此时创建了文件
file.createNewFile();

System.out.println(file.exists()); //true

System.out.println(file.isFile()); //true

System.out.println(file.isDirectory()); //false

方法

描述
boolean  delete()    根据File对象,删除该⽂件。成功删除后返回true
void  deleteOnExit()    根据File对象,标注⽂件将被删除,删除动作会到JVM运⾏结束时(进程结束)才会进行
String[]  list()    返回File对象代表的⽬录下的所有⽂件名
File[]  listFiles()    返回File对象代表的⽬录下的所有⽂件,以File对象表⽰
boolean  mkdir()  创建File对象代表的⽬录,只能创建一级目录
boolean  mkdirs()    创建File对象代表的⽬录,可以创建多级目录
public static void main(String[] args) {
    File f = new File("./testDir");
    f.mkdir();
    System.out.println(f.isDirectory());
}


方法描述
boolean  renameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
boolean  canRead()判断用户是否对⽂件有可读权限
boolean  canWrite()判断用户是否对⽂件有可写权限
public static void main(String[] args) {
    File f = new File("./testDir/test2.txt");
    File file = new File("./test.txt");
    file.renameTo(f); //将原本路径下的文件修改为新路径下的新名字的文件
}

4.2 针对文件内容

Java中通过流这样的一组类,进行文件内容的操作

字节流:以字节为单位读写数据(二进制文件) → InputStream、OutPutStream

字符流:以字符为单位读写数据(文本文件)→ Reader、Writer

4.2.1 InputStream

InputStream是一个抽象类,不能实例化,但标准库已经提供好了子类

InputStream inputStream = new FileInputStream("./testDir/test2.txt");

这里构造方法填写参数:

  1. 填写字符串表示的文件路径(绝对/相对均可)
  2. 填写File对象

这行代码就会通过构造方法打开一个文件,如果打开文件成功,就可以进行后续读取操作了

1)int read()

一次读取一个字节,将读取到的内容返回

此处一次读取一个字节,为什么返回值类型不是byte,而是int:

read()方法,如果返回值类型是byte,byte的取值范围为-128到127,但是需要一种机制表示输入流结束,这里通过返回-1表示流结束,但是-1在-128到127属于有效值,所以要改为int扩大取值范围,让0到255表示有效值,-1表示流结束

代码案例:

事先在test2.txt里面写上hello

public static void main(String[] args) throws IOException {

    InputStream inputStream = new FileInputStream("./testDir/test2.txt");
    while (true) {
        int b = inputStream.read();
        if (b == -1) {
            break;
        }
        System.out.printf("0x%x ", b);
    }
}

将16进制转化为10进制,正好对应ASCIIdh、e、l、l、o

2)int read ( byte[ ]  b )

将读到的数据填写到byte数组中,返回值代表实际读取了多少个字节,返回-1代表读完了

代码案例:

    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("./testDir/test2.txt");
        while (true) {
            byte[] b = new byte[1024];
            int n = inputStream.read(b);
            if (n == -1) {
                break;
            }
            for (int i = 0; i < n; i++) {
                System.out.printf("0x%x ", b[i]);
            }

        }

    }

每次读完都会将数据存放在b中,如果文件数据过多,每次读取都会填满数组,然后循环多次

3)int  read ( byte[ ]  b,  int  off,  int  len )

读取文件中数据并填充数组的一部分,从 offset 开始,填充 len 这么长

4.2.2 关闭文件

打开文件的时候,操作系统内核会向该进程中的PCB结构体中,给文件描述符表里添加一个元素,这个元素表示当前打开的文件的各种信息(文件描述符表相当于一个顺序表,但不能扩容),当使用close关闭文件时,文件描述符表里对应的元素就会被释放掉

如果文件只开不关,文件描述符表就会被占满,一旦占满,再次打开文件,就会打开失败

我们刚刚写的程序,没有关闭文件好像没什么问题,这是因为代码很快就执行完了,进程也就结束了,进程一结束,pcb就会被释放掉,就没有什么机会将文件描述表占满,

打开文件和关闭文件之间会有一系列的操作,有的操作可能会return或抛出异常,导致文件没有及时关闭,和之前ReentrantLock类似:将解锁操作写到finally里;这里关闭文件也可以写到finally里

除此之外还有一种方法:try  with  resource

将流对象的创建写到 try ( ) 中,代码执行出了 try { } 后,就会自动调用关闭文件的操作,注意,写进 try ( ) 中的流对象的类必须实现了 Closeable 接口

try (InputStream inputStream = new FileInputStream("D:/test.txt")) {
    byte[] b = new byte[1024];
    int n = inputStream.read(b);
    for (int i = 0; i < n; i++) {
        System.out.printf("0x%x ", b[n]);
    }
}

4.2.3 OutputStream

方法描述
void  write ( int  b )一次写一个字节
void  write ( byte[ ]  b )一次写若干个字节,把数组b里的所有字节都写入文件中
void  write ( byte[ ]  b,  int  off,  int  len )一次写若干个字节,从b数组的off下标开始写,写len个字节
//OutputStream 为抽象类,使用子类 FileOutputStream 实例化
OutputStream out = new FileOutputStream("./testDir/test2.txt");
//连续写3个 a
out.write(97);
out.write(97);
out.write(97);

按照写方式打开文件时,会把文件原有的内容清空(不是write清空的,而是打开文件时清空的)可以使用追加写

新数据会在原数据的末尾:

4.2.4 Reader

方法描述
int  read  ( )一次读取一个字符,返回-1表示读取结束
int  read  ( char[ ]  cbuf )读取若干个字符到cbuf数组中,返回-1表示读取结束

事先在test2.txt中写上:你好世界

public static void main(String[] args) {
    //Reader为抽象类,使用子类 FileReader 实例化
    try (Reader reader = new FileReader("./testDir/test2.txt")) {
        while (true) {
            int b = reader.read(); //一次读取一个字符
            if (b == -1) {
                break;
            }
            char ch = (char)b;
            System.out.println(ch);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

  • 问题:读出来的内容是中文,一个中文字符占3个字节,但Java中char类型占2个字节,为什么可以读取成功?
  • 答:虽然文件中原始数据是3个字节一个字符,read操作读取的时候,能够识别文件是utf-8格式,读的是3个字节,返回成一个char的时候,会将utf-8转成unicode,这样一个字符就占2个字节

4.2.5 Writer

方法描述
void  write( int  c )一次写一个字符
void  write( char[ ]  cbuf )将数组 cbuf 中的字符写入文件
void  write( String  str )将字符串 str 写入字符
public static void main(String[] args) {
    try (Writer writer = new FileWriter("./testDir/test2.txt", true)) {
        writer.write('0');

        char[] c = {'\n', '数', '组'};
        writer.write(c);

        String str = "\n字符串";
        writer.write(str);

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


🙉本篇文章到此结束


原文地址:https://blog.csdn.net/m0_74270127/article/details/144311249

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!