【JavaEE】文件操作

    🔥个人主页: 中草药

🔥专栏:【Java】登神长阶 史诗般的Java成神之路


🔭一.文件和IO

认识文件

        在计算机科学中,文件是一种存储在某种持久性存储介质(如硬盘、固态硬盘或闪存等)上的数据集合。文件可以包含各种类型的信息,包括文本、图像、音频、视频、程序代码等等。每个文件通常都有一个唯一的名称来标识它,并且可能有一个扩展名来表明它的类型。

        文件是组织和管理数据的基本单位之一。它们由操作系统进行管理和控制,通过文件系统来实现。文件系统定义了如何命名、存储、检索以及组织文件的方法。以下是文件的一些基本概念:

  • 文件名:用于唯一标识文件的名称。
  • 路径:描述文件在目录结构中的位置。
  • 目录(文件夹):用于组织文件的一种方式,可以包含其他文件或子目录。
  • 属性:文件的元数据,例如创建日期、修改日期、大小、权限等。
  • 权限:控制谁可以访问文件以及如何访问的设置。

文件的操作包括但不限于创建、读取、更新、删除、复制、移动等。不同类型的文件需要不同的应用程序或工具来打开和处理。例如,文本文件可以用任何文本编辑器打开,而图像文件则需要图像查看器或编辑软件来处理。

了解这些基本概念对于有效地使用计算机资源非常重要。如果您有特定类型的文件或与文件相关的操作想要了解,请告诉我,我可以提供更详细的信息。

目录

        在计算机文件系统中,目录(有时也称为文件夹)是用于组织和管理文件的一种逻辑容器。目录可以看作是树形结构的一个应用实例,其中每个目录都可以包含文件和其他目录(子目录)。文件系统的目录结构遵循以下特点:

  1. 根目录:所有其他目录和文件的最高级目录,通常表示为 /
  2. 子目录:位于另一个目录内的目录。
  3. 路径:用于定位文件或目录的唯一标识符,由一系列目录名组成,目录之间用斜杠 / 分隔。
    • 绝对路径:从根目录开始的路径。
    • 相对路径:相对于当前工作目录的路径。

假设我们有一个简单的文件系统树形结构如下所示:

/
├── Documents
│   ├── Report.txt
│   └── Photos
│       └── Holiday.jpg
└── Music
    ├── Song1.mp3
    └── AlbumArt.jpg

在这个例子中:

  • / 是根目录。
  • Documents 和 Music 都是根目录下的直接子目录。
  • Photos 是 Documents 目录下的子目录。
  • Report.txt 和 Holiday.jpg 分别是 Documents 和 Photos 目录下的文件。

文件路径

文件路径是指在文件系统中用于唯一标识文件或目录的位置信息。路径通常由一系列目录名称组成,并使用分隔符(通常是斜杠 / 或反斜杠 \)连接起来。文件路径有两种主要类型:绝对路径和相对路径。

绝对路径

绝对路径是从文件系统的根目录开始的完整路径,它明确地指示了文件或目录的确切位置。在大多数现代操作系统中,绝对路径通常以根目录符号 / 开始。

例子

 在Windows中,绝对路径看起来像这样:

C:\Users\user\Documents\example.txt

相对路径

相对路径是从当前工作目录开始的路径。它使用相对于当前位置的目录名称来指定文件或目录的位置。相对路径不从根目录开始,而是从当前目录开始。

例子

假设当前工作目录为 /home/user/documents,那么文件 example.txt 的相对路径可能是这样的:

example.txt

如果 example.txt 存在于同一目录下的子目录 subfolder 中,则相对路径将是:

subfolder/example.txt

路径中的特殊符号

在文件路径中还有一些特殊的符号用于表示特定的含义:

  • . 表示当前目录。
  • .. 表示上一级目录。

使用特殊符号的例子

假设当前工作目录为 /home/user/documents,要访问 /home/user/ 下的文件 notes.txt,可以使用相对路径:

../notes.txt

IO

输入/输出 (I/O)

输入/输出(Input/Output,简称I/O)是指计算机系统与外部环境之间进行数据交换的过程。这种交互既可以是硬件层面的,也可以是软件层面的。I/O 涉及到计算机系统与外部设备之间的数据传输,这些外部设备可以是键盘、鼠标、显示器、打印机、磁盘驱动器等。

I/O 的分类

I/O 可以根据不同的标准进行分类:

  1. 按方向分类

    • 输入 (Input):数据从外部设备流向计算机系统。例如,用户通过键盘输入数据。
    • 输出 (Output):数据从计算机系统流向外部设备。例如,计算机将数据发送给打印机以打印文档。
  2. 按设备类型分类

    • 标准输入/输出
      • 标准输入 (stdin):默认为键盘输入。
      • 标准输出 (stdout):默认为屏幕输出。
      • 标准错误输出 (stderr):默认为屏幕输出,通常用于显示错误信息。
    • 文件 I/O:涉及从文件读取数据或将数据写入文件。
    • 网络 I/O:涉及通过网络协议读取或写入数据,如HTTP、FTP等。
    • 设备 I/O:涉及与硬件设备的交互,如磁盘驱动器、打印机等。
  3. 按操作类型分类

    • 同步 I/O:数据的读写操作会阻塞进程或线程直到操作完成。
    • 异步 I/O:数据的读写操作不会阻塞进程或线程,允许在后台完成操作。

I/O 的实现

在不同的编程语言中,I/O 操作的实现方式各不相同。下面是一些常见的编程语言中 I/O 操作的例子:

  1. C/C++

    • 使用 <stdio.h> 库进行标准输入输出。
    • 使用 fopenfclosefreadfwrite 等函数进行文件 I/O 操作。
  2. Java

    • 使用 java.io 包中的类,如 InputStreamOutputStreamFileReaderFileWriter 等。
    • 使用 System.in 和 System.out 进行标准输入输出。
  3. Python

    • 使用内置的 open() 函数进行文件 I/O 操作。
    • 使用 sys.stdin 和 sys.stdout 进行标准输入输出。

🧪二.File

在 Java 中,java.io.File 类是一个非常重要的类,用于处理文件和目录。它提供了许多方法来获取文件信息、创建和删除文件以及目录等。File 类本身并不直接读写文件内容,而是提供了文件和目录的元数据操作,以及与其他 I/O 类(如 InputStreamOutputStream)的交互接口。

构造方法

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

主要方法

方法名描述返回值类型
boolean canExecute()判断此抽象路径名表示的文件是否可执行。boolean
boolean canRead()判断此抽象路径名表示的文件是否可读。boolean
boolean canWrite()判断此抽象路径名表示的文件是否可写。boolean
long length()返回此抽象路径名表示的文件的长度(以字节为单位)。long
String getName()返回此抽象路径名的文件名。String
String getParent()返回此抽象路径名的父目录的路径名字符串,如果没有父目录,则返回 nullString
String getPath()返回此抽象路径名的路径名字符串。String
String getAbsolutePath()返回此抽象路径名的绝对路径名字符串。String
String getCanonicalPath()返回此抽象路径名的规范路径名字符串。String
boolean createNewFile()创建由此抽象路径名表示的新空文件。boolean
boolean delete()删除由此抽象路径名表示的文件或目录。boolean
boolean mkdir()创建由此抽象路径名表示的目录。boolean
boolean mkdirs()创建由此抽象路径名表示的目录及其所有不存在的父目录。boolean
boolean renameTo(File dest)将由此抽象路径名表示的文件重新命名为此参数表示的文件。boolean
boolean setExecutable(boolean executable)设置此抽象路径名表示的文件的可执行属性。boolean
boolean setReadable(boolean readable)设置此抽象路径名表示的文件的可读属性。boolean
boolean setWritable(boolean writable)设置此抽象路径名表示的文件的可写属性。boolean
long lastModified()返回此抽象路径名表示的文件最后一次修改的时间。long
boolean exists()测试由此抽象路径名表示的文件或目录是否存在。boolean
boolean isDirectory()测试由此抽象路径名表示的文件是否是一个目录。boolean
boolean isFile()测试由此抽象路径名表示的文件是否是一个普通文件。boolean
boolean isHidden()测试由此抽象路径名表示的文件是否是一个隐藏文件。boolean
String[] list()返回一个字符串数组,包含此抽象路径名表示的目录中的文件和目录。String[]
File[] listFiles()返回一个 File 数组,包含此抽象路径名表示的目录中的文件和目录。File[]

注意事项

  1. 异常处理:当执行文件操作时,通常需要处理 IOException
  2. 文件路径:在跨平台环境中使用 File 类时,需要确保路径正确处理不同操作系统中的路径分隔符(Windows 使用 \,Unix/Linux 使用 /)。
  3. 性能考虑:频繁的文件操作可能会导致性能下降,尤其是在高负载的应用场景中。

 🔬三.文件内容的读写——数据流

在 Java 中,读写文件内容通常通过数据流(Data Streams)来完成。数据流是用于在源和目标之间传输数据的对象。Java 提供了多种流来处理不同的数据类型和操作,包括字节流和字符流。

字节流 vs. 字符流

  • 字节流:处理原始字节数据,通常用于处理二进制文件。

    • InputStream:读取字节流的基类。
    • OutputStream:写入字节流的基类。
  • 字符流:处理字符数据,通常用于处理文本文件。

    • Reader:读取字符流的基类。
    • Writer:写入字符流的基类。

InputStream

在 Java 中,InputStream 是所有字节输入流的基类。它定义了一系列方法用于从数据源读取字节,并提供了基本的字节流操作。InputStream 是一个抽象类,不能直接实例化,但它定义了所有子类必须实现的方法。

主要方法

方法名描述返回值类型
int read()从输入流中读取下一个字节的数据。如果没有更多的字节可以读取,则返回 -1int
int read(byte[] b)从输入流中读取最多 b.length 个字节的数据到字节数组 b 中。返回实际读取的字节数,如果没有更多的字节可以读取,则返回 -1int
int read(byte[] b, int off, int len)从输入流中读取最多 len 个字节的数据到字节数组 b 中,从索引 off 开始存放。返回实际读取的字节数,如果没有更多的字节可以读取,则返回 -1int
void close()关闭此输入流并释放与之关联的所有系统资源。void

其他方法 

方法名描述返回值类型
boolean markSupported()返回 true 如果此输入流支持标记和重置方法,否则返回 falseboolean
void mark(int readlimit)设置此输入流的标记点。参数 readlimit 指定在下一次调用 reset() 方法之前可以读取的字节数,而不使标记失效。void
void reset()使此输入流重置到上次调用 mark() 方法设置的标记点。如果此流不支持标记和重置,则抛出 IOExceptionvoid
long skip(long n)跳过并丢弃输入流中的 n 个字节。返回实际跳过的字节数。long
void transferTo(OutputStream out)将此输入流中的剩余内容传输到指定的输出流 out。自 Java 7 起可用。void

 举例

public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("./test.txt")) {
            while (true) {
                byte[] buffer = new byte[1024];
                // n 返回值表示 read 操作, 实际读取到多少个字节.
                int n = inputStream.read(buffer);
                if (n == -1) {
                    break;
                }
                for (int i = 0; i < n; i++) {
                    System.out.printf("0x%x\n", buffer[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

子类

InputStream 类有许多子类,用于处理不同的数据源和流类型。这里列举几个常见的子类:

  1. FileInputStream

    • 用于读取文件中的字节数据。
  2. ByteArrayInputStream

    • 用于从字节数组中读取数据。
  3. BufferedInputStream

    • 提供了一个带有缓冲区的 InputStream 实现,可以提高读取效率。
  4. ObjectInputStream

    • 用于反序列化对象。
  5. PipedInputStream

    • 与 PipedOutputStream 一起用于线程间的通信。

使用技巧

  1. 资源管理:使用 try-with-resources 语句可以自动关闭流,避免资源泄露。
  2. 异常处理:在处理文件流时,通常需要捕获和处理 IOException
  3. 缓冲:使用 BufferedInputStream 可以提高读取效率。

OutputStream

在 Java 中,OutputStream 类是所有字节输出流的基类。它定义了一系列方法用于向目的地写入字节数据,并提供了基本的字节流操作。OutputStream 是一个抽象类,不能直接实例化,但它定义了所有子类必须实现的方法。

主要方法

法名描述返回值类型
void write(int b)向输出流写入指定的字节。参数 b 必须是一个 0 到 255 之间的整数。void
void write(byte[] b)向输出流写入 b.length 个字节的数据。参数 b 是一个字节数组。void
void write(byte[] b, int off, int len)向输出流写入 len 个字节的数据,从字节数组 b 的索引 off 开始。void
void flush()清空此输出流并强制写出所有缓冲的输出字节。void
void close()关闭此输出流并释放与之关联的所有系统资源。void
重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的⼀个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域⼀般称为缓冲区。但造成⼀个结果,就是我们写的数据,很可能会遗留⼀部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。

举例

import java.io.FileOutputStream;
import java.io.IOException;

public class OutputStreamExample {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("example.txt")) {
            // 写入一些文本
            byte[] data = "Hello, this is a test.".getBytes();
            fos.write(data);

            // 写入单个字节
            fos.write(10); // ASCII for newline character
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

子类

OutputStream 类有许多子类,用于处理不同的数据目的地和流类型。这里列举几个常见的子类:

  1. FileOutputStream

    • 用于写入文件中的字节数据。
  2. ByteArrayOutputStream

    • 用于向字节数组写入数据。
  3. BufferedOutputStream

    • 提供了一个带有缓冲区的 OutputStream 实现,可以提高写入效率。
  4. ObjectOutputStream

    • 用于序列化对象。
  5. PipedOutputStream

    • 与 PipedInputStream 一起用于线程间的通信。

使用技巧

  1. 资源管理:使用 try-with-resources 语句可以自动关闭流,避免资源泄露。
  2. 异常处理:在处理文件流时,通常需要捕获和处理 IOException
  3. 缓冲:使用 BufferedOutputStream 可以提高写入效率。

🔭四.操作实例

实例1

复制初文件

代码

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

public class demo2 {
    /**
     * 复制文件
     * @param args
     */
    public static void main(String[] args) {
        //1.输入路径,并且做校验
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入源文件的路径");
        String srcPath = sc.nextLine();
        File srcFile = new File(srcPath);
        if (!srcFile.isFile()) {
            System.out.println("源文件路径不存在");
            return;
        }

        System.out.println("请输入目标文件的路径");
        String destPath = sc.nextLine();
        File destFile = new File(destPath);
        if (!destFile.getParentFile().isDirectory()) {
            System.out.println("目标文件路径不存在");
            return;
        }

        //2.执行复制过程
        try (InputStream inputStream = new FileInputStream(srcFile);
             OutputStream outputStream = new FileOutputStream(destFile)) {
            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();
        }
        System.out.println("复制文件完成");
    }
}

运行结果

初始

运行后

实例2

在目录中搜索含有相关关键字的文件

代码

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

public class demo3 {
    private static void scan(File currentFile,String key){
        if (!currentFile.isDirectory()){
            return;
        }

        File[] files=currentFile.listFiles();
        if (files.length==0||files==null){
            return;
        }
        for (File f:files) {
            if (f.isFile()){
                doSearch(f,key);
            }else{
                scan(f,key);
            }
        }
    }

    private static void doSearch(File f,String key){
        StringBuilder stringBuilder=new StringBuilder();

        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);
                stringBuilder.append(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (stringBuilder.indexOf(key)==-1){
            //未找到
            return;
        }
        System.out.println("找到了匹配的文件:"+f.getAbsolutePath()  );
    }

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入你要搜索的路径:");
        String rootPath=sc.nextLine();
        File rootFile=new File(rootPath);
        if (!rootFile.isDirectory()){
            System.out.println("要搜索的路径有误");
            return;
        }
        System.out.println("请输入你要查询的内容");
        String key=sc.next();

        scan(rootFile,key);
    }
}

运行结果

🧬五.总结与反思

大鹏一日同风起,扶摇直上九万里。

        在深入学习Java中的文件操作之后,我对文件读写、文件流、路径处理等方面有了更深刻的理解。通过实践和探索,我对文件操作有了更全面的认识。

        通过这次学习,我对Java中的文件操作有了更深刻的理解,并认识到在实际应用中需要综合考虑多种因素。在未来的开发工作中,我将继续深入研究这些知识点,并努力将它们应用到实践中去。学习文件操作不仅提高了我的编程技能,还让我意识到了在设计文件处理程序时需要考虑的诸多因素。


🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸

  • 70
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 77
    评论
评论 77
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值