文件操作--IO

目录

1.文件

1.1狭义上的文件

1.2广义上的文件

2.文件路径

2.1路径组成

2.2绝对路径

2.3相对路径

2.4文件类型

2.4.1按用途分类

2.4.2按存储方式分类

3.java中操作文件

3.1File文件概述

3.1.1属性

3.1.2构造方法

3.1.3常用方法

4.文件内容的读写--数据流

4.1 InputStream

4.2 OutputStream

4.3 字符流

4.4 Scanner进行字符读取

5.案例

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

5.2进行普通文件的复制

6.小结


 

1.文件

1.1狭义上的文件

狭义的文件指的是存储在计算机存储设备上的数据集合,具有名称,扩展名,内容,属性等基本特征,是操作系统管理的基本单位

1.2广义上的文件

广义的文件不仅包括狭义的文件,还涵盖逻辑上可被当作文件处理的抽象数据单元,甚至包括内存中的临时数据、网络传输的数据流、设备接口的虚拟文件等。泛指计算机中很多软硬件资源.

2.文件路径

计算机中,文件路径用于唯一标识文件或目录在存储设备上的位置,不同操作系统的路径表示方式略有不同,但是核心逻辑相似

2.1路径组成

根目录:路径的起点(如C:\或/)

目录分隔符:

Windows:反斜杠 \(如 C:\Users\Alice),但是Windows也支持/作为分隔符

Linux/macOS:正斜杠 /(如 /home/alice

2.2绝对路径

从根目录开始,完整描述文件位置.适用于精准定位文件,不受当前工作目录的影响

2.3相对路径

基于当前工作目录(每个程序运行的时候,都有一个工作目录),描述文件的相对位置.

常用的符号:

  1. .表示当前目录(如 ./file.txt)
  2. ..表示上一级目录../images/photo.jpg

示例:

假设当前的工作目录,是d:/tmp,我们需要定位到111这个文件

  1. 如果工作目录是d:/  相对路径写成 ./tmp/111
  2. 如果工作目录是d:/tmp  相对路径写成 ./111
  3. 如果工作目录是d:/tmp/222  相对路径写成 ../111 (..表示当前目录的上级目录)
  4. 如果国祚目录是d:/tmp/222/bb 相对路径写成 ../../111

2.4文件类型

2.4.1按用途分类

类型常见扩展名说明示例程序
文本文档.txt, .docx存储纯文本或格式化文字记事本、Word
图片文件.jpg, .png存储图像数据Photoshop、画图
音频文件.mp3, .wav存储声音音乐播放器、Audacity
视频文件.mp4, .avi存储视频VLC、PotPlayer
可执行文件.exe, .app运行程序或安装软件Windows/macOS 系统
压缩文件.zip, .rar打包和压缩多个文件WinRAR、7-Zip
网页文件.html, .css网站代码浏览器、VS Code
数据库文件.sql, .db存储结构化数据MySQL、SQLite
脚本文件.py, .sh存储可执行脚本Python、Bash

2.4.2按存储方式分类

类型说明示例
文本文件以ASCII/Unicode编码存储.txt, .csv, .json
二进制文件以机器可读的二进制格式存储.exe, .jpg, .mp3

3.java中操作文件

java中分别可以针对文件系统操作和文件内容操作

3.1File文件概述

3.1.1属性

修饰符及类型属性说明
static StringpathSepartor依赖于系统的分隔符路径
static charpathSepartor依赖于系统的分隔符路径

pathSepartor:File里面的一个静态变量,就是/或者\,跟着系统走的

3.1.2构造方法

构造方法说明
File(String pathname)通过路径字符串创建 File 对象(可以是文件或目录)。
File(String parent, String child)在父目录路径 parent 下,指定子路径 child 创建文件。
File(File parent, String child)在父 File 对象下,指定子路径 child 创建文件。

3.1.3常用方法

文件目录的基本信息

方法说明
boolean exists()判断文件或目录是否存在。
boolean isFile()判断是否是文件(不是目录)。
boolean isDirectory()判断是否是目录。
String getName()返回文件名(含扩展名,如 example.txt)。
String getPath()返回路径字符串(构造时的路径)。
String getAbsolutePath()返回绝对路径。
long length()返回文件大小(字节数),目录返回 0
long lastModified()返回最后修改时间(时间戳)。

文件/目录操作

方法说明
boolean createNewFile()创建新文件(若文件已存在则返回 false)。
boolean delete()删除文件或空目录。
boolean mkdir()创建单级目录。
boolean mkdirs()创建多级目录(如 a/b/c)。
boolean renameTo(File dest)重命名或移动文件。

目录内容列表

方法说明
String[] list()返回目录下的文件名数组。
File[] listFiles()返回目录下的 File 对象数组。

权限与属性

方法说明
boolean canRead()文件是否可读。
boolean canWrite()文件是否可写。
boolean canExecute()文件是否可执行。
boolean setReadOnly()设置为只读。

示例:

1.文件操作方法演示

package io;

import java.io.File;
import java.io.IOException;

public class IODemo1 {
    public static void main(String[] args) throws IOException {
        File file = new File("./test.txt");//不要求这里真的有test.txt
        System.out.println(file.getName());
        System.out.println(file.getParent());
        System.out.println(file.getPath());
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getCanonicalPath());

    }
}

  1. 第一行,返回的是文件名(不包含路径)。
  2. 第二行,返回的是父目录路径。
  3. 第三行,返回的是构造 File 对象时的路径字符串。
  4. 第四行,返回绝对路径(但不会解析 ...)。
  5. 第五行,返回规范化的绝对路径(解析 ...,并去除冗余符号)。

2.创建一个新文件

自动创建了test.txt文件

3. 删除文件

package io;

import java.io.File;

public class IODemo3 {
    public static void main(String[] args) {
        File file = new File("./test.txt");
        file.delete();
    }

}

 

删除了文件

4.创建多级目录

package io;

import java.io.File;

public class IODemo4 {
    public static void main(String[] args) {
        File dir = new File("./text/aaa/bbb");
        //mkdir只能创建一级目录
        //mkdirs 可以创建多级目录
        dir.mkdirs();

    }
}

 我们发现,会自动帮我创建一个多级目录

5.文件改名

package io;

import java.io.File;

public class IODemo5 {
    public static void main(String[] args) {
        File file = new File("./text");
        File dest = new File("./textAAA");
        file.renameTo(dest);
    }
}

 

4.文件内容的读写--数据流

文件读写时编程中的基础操作,涉及数据的输入输出流

输入流:从文件或其他来源读取数据的通道

输出流:向文件或者其他目标写入数据的通道

java标准库的流对象从类型上可以分为两大类:

  1. 字节流,操作二进制数据
    1. InputStream FileInputStream
    2. OutputStream FileOutputStream
  2. 字节流,操作文本数据
    1. Reader FileReader
    2. Writer FileWriter

4.1 InputStream

方法

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

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class IODemo6 {
    //字节流读取文件
    public static void main(String[] args) throws IOException {
        //创建InputStream对象的时候,使用绝对路径和相对路径都是一样的
        InputStream inputStream =   new FileInputStream("d:/test.txt");
        while(true){
            //如果第一轮没有读完,则循环下一轮继续读取
            //但是第二轮读的数据,会覆盖第一轮读的数据
            byte[] buffer = new byte[1024];
            //变量名buffer(缓冲区),buffer存在就是为了提高IO操作的效率
            int len = inputStream.read(buffer);
            //这里传入的是一个刚准备的数组,把这个数组交给read,让read方法对该数组内部进行一个填充
            //read的返回值,read尽可能把参数传进来的数组给填满
            //read尽可能读取1024个字节,填充到数组里
            //但是文件剩余长度有限,如果文件剩余长度超过1024,则会填满
            //如果剩余不足,则会有多少就填写多少,返回当前实际读取长度
            System.out.println("len: " + len);
            if (len == -1){
                break;
            }
            for (int i = 0; i < len; i++) {
                System.out.printf("%x\n" , buffer[i]);
            }
        }
        inputStream.close();
    }
}

注意:read方法会返回当前实际读取的长度

如代码所示,我们使用read从test.txt文件中读取数据

 我们可以看到返回了hello world中每一个字母的ascii码表.(16进制)

4.2 OutputStream

方法

方法

功能说明

示例

void write(int b)

写入单个字节(低8位有效,高24位忽略)

os.write(65); // 写入'A'的ASCII码

void write(byte[] b)

写入整个字节数组

os.write("Hello".getBytes());

void write(byte[] b, int off, int len)

写入字节数组的指定范围(从off开始,长度len

os.write(data, 0, 5); // 写入前5字节

void flush()

强制将缓冲区数据写入目标(如磁盘/网络)

os.flush(); // 确保数据立即写入

void close()

关闭流并释放资源(必须调用)

os.close(); 或使用 try-with-resources

package io;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class IODemo7 {
    //写文件
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("d:/test.txt");
        //OutputStream默认情况下,打开一个文件,会先清空文件原来有的内容
        /*
        如果不想清空,流对象还提供了一个"追加写"对象
        通过这个把新的内容追加写在后面
         */
        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);
        outputStream.write(100);

        outputStream.close();
        
    }
}

  1.  这里的close操作含义是关闭文件,线程中的PCB中有一个重要的文件,文件描述表,记录该进程打开了哪些文件 ,打开文件操作,就会在文件描述符表里,申请一个位置,把这个信息放进去,每次关闭文件操作,也会把这个表对应的表项给释放掉
  2. 如果没写close,则这个表项没有及时释放,虽然java有GC,但是回收不一定及时;如果不手动释放,可能会把表项很快占满,因为这个数组不能自动扩容,他是存在上限的
  3. close一般来时是要执行的,但是一个程序,这里的文件对象自始至终要使用,不关闭也问题不大,随着进程结束,PCB销毁,文件描述表也就销毁了,对应资源也就自动回收了

注意: Input(输入)和 Output(输出) 的命名源于 程序视角 而非用户视角。这种命名约定是为了统一描述数据流动方向

方向程序视角用户视角类比
Input数据流入程序(读操作)程序“读取”外部数据像用吸管喝水(吸入)
Output数据流出程序(写操作)程序“写入”外部存储像用喷壶浇水(喷出)

    那么,在这段代码中我们考虑这样几个问题,

1.如果我们忘记写close,会发生什么情况

如果没哟close,对应的表项没有及时释放,虽然java有GC,GC操作会在回收这个OutputStream对象的时候去完成这个释放操作,但是这个GC不一定及时,所以不手动释放,意味着文件描述符很可能很快就被沾满

2. 如果我们的程序出现bug,无法执行到close怎么办?

   我们提出一种更加推荐的写法,在java中被称为 try with resources,这是 Java 7 引入的一种异常处理语法,用于自动管理资源(如文件流、数据库连接等需要关闭的资源)。它简化了资源管理代码,避免了传统 try-catch-finally 块中的繁琐关闭操作。

但是这个有使用条件:资源类必须实现AutoCloseable或者Closeable接口,可以在try后的括号里声明多个资源,用分号分隔

package io;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class IODemo7_1 {
    public static void main(String[] args) throws IOException {
        try(OutputStream outputStream = new FileOutputStream("d:/testt.txt")){
        //实现了Closeable接口的类才可以放到try的()中被自动关闭
        //这个接口提供的方法就是close
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
        }
    }
}

4.3 字符流

用法和字节流基本差不多

package io;

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

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

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("d:/test.txt")) {
            writer.write("hello world");
            //写操作其实是先写到缓冲区里面(缓冲区有多种,自己代码里面,标准库里面,操作系统内核里面)
            //写操作执行完了,内容还可能在缓冲区里面,还没有真的进入硬盘
            writer.close();
            //close操作,会触发缓冲区的刷新
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4.4 Scanner进行字符读取

对字符类型直接用InputStream进行读取是非常麻烦并且是非常困难的,所以我们可使用Scanner完成这项工作.

我们这里仍然对d:test.txt的数据进行读取操作

package io;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class IODemo10 {
    public static void main(String[] args) {
        //Scanner scanner = new Scanner(System.in);
        File file = new File("d:test.txt");
        if (!file.exists()) {
            System.out.println("文件不存在!");
            return;
        }
        try(InputStream inputStream = new FileInputStream("d:test.txt")){
            Scanner scanner = new Scanner(inputStream);
            //此时内容就从文件中读取了
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                System.out.println(line);
            }
            //由于在try就已经对inputStream对象进行了关闭,里面的scanner不关闭也没事
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
  1.  hasNextLine():检查输入源中是否还有下一行(包括空行,如果有返回true,如果没有返回false
  2. nextLine():读取当前行的剩余内容(包括换行符前的所有字符),并将指针移动到下一行开头,返回类型为String

我们这里可以看到,在控制台直接输出了我们当前文档中的数据 .

5.案例

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

package io;

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("[scan.dir]" + root.getAbsolutePath());
        //先列出root下面的文件和目录
        File[] files = root.listFiles();
        //如果当前目录下没有东西,是一个空目录
        //结束继续递归
        if (files == null){
            return;
        }

        for (File file : files){
            if (file.isDirectory()){
                scanDir(file,nameToDelete);
            }else {
                if (file.getName().contains(nameToDelete)){
                    System.out.println("确认是否要删除 " + file.getAbsolutePath() + "吗?");
                    String choice = scanner.next();
                    if (choice.equals("Y") || choice.equals("y")){
                        file.delete();
                        System.out.println("删除成功!");
                    }else {
                        System.out.println("删除取消");
                    }
                }
            }
        }
    }
}

 


 代码执行流程:

  1. 入口检查
    1. 打印当前扫描目录路径:[scan.dir] + 绝对路径
    2.  调用listFiles()获取目录下所有文件和子目录,如果目录为空或不可访问(files == null),立即返回
  2. 遍历每个条目:for(File file : files){}
    1. 如果是目录:
      if (file.isDirectory()){
          scanDir(file,nameToDelete);
    2. 如果是文件
      if (file.getName().contains(nameToDelete)){
          //代码逻辑
      }
  3. 文件删除确认流程:发现匹配文件时:打印确认提示,等待用户输入,判断输入
  4. 递归控制:每层递归处理完当前目录所有条目后自动返回

5.2进行普通文件的复制

package io;

import java.io.*;
import java.util.Scanner;

public class IODemo12 {
    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)){
            //OutputStream 在写文件的时候,文件不存在,就会抛出异常

            //进行拷贝操作
            while (true){
                int b = inputStream.read();
                if (b == -1){
                    break;
                }
                outputStream.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码执行流程:

  1. 资源初始化阶段
    try(InputStream inputStream = new FileInputStream(srcFile);
        OutputStream outputStream = new FileOutputStream(destFile)){
    1. 先创建输入流(读取源文件)
    2. 再创建输出流(写入目标文件,若目标文件不存在会自动创建
    3. 若目标文件不存在,会抛出异常
  2. 字节拷贝阶段
    1. read()每次读取1字节,返回int,到达文件末尾时返回-1
    2. write(int b)写入低8位,忽略高24位,数据会先写入缓冲区,满后会刷入磁盘
  3. 资源关闭阶段
    1. 自动关闭,因为我们的InputStream和OutputStream,符合try with resource特性
    2. 先关闭后声明的OutputStream再关闭InputStream

6.小结

    文件的基本概念:文件是存储数据的基本单位,包含文件名、扩展名、内容及属性(如大小、权限)。
    文件路径:分为绝对路径和相对路径,不同操作系统(Windows/Linux/macOS)的路径表示方式不同。
    文件类型:通过扩展名区分(如 .txt、.jpg),决定了文件的打开方式和用途。
    文件操作:通过编程语言(如Java的File类)可以获取文件信息(路径、大小)、创建/删除文件、遍历目录等。
    文件读写与数据流:
        Java 使用 InputStream(输入流)读取文件,OutputStream(输出流)写入文件。
        字节流(FileInputStream/FileOutputStream)适合二进制文件,字符流(Reader/Writer)适合文本文件。
        缓冲流(如 BufferedInputStream)能大幅提升 I/O 性能。
    try-with-resources:
        Java 7+ 引入的语法,自动管理资源(如文件流),避免手动 close(),代码更简洁安全。
        资源需实现 AutoCloseable 接口(如所有流类)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值