JAVA文件I/O

发布于:2025-04-20 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

一、三种路径的分类:

1、绝对路径:

2、相对路径:

3、基准目录:

二、文件的种类:

三、利用JAVA操作文件:

1、File类的构造方法:

2、File 类方法的使用:

使用例子:

四、利用JAVA操作文件内容:

1、字节流:

读取操作:

实例化对象:

 FileInputStream 核心方法:

输出操作:

实例化对象 

 FileOutputStream 核心方法:

2、字符流:

读取操作:

实例化对象:

 FileReader 核心方法:

输出操作:

实例化对象:

 FileWriter 核心方法:

五、使用文件操作的案例:

1、根据用户输入的目录和文件名,来进行文件的查找和删除

2、基根据用户输入的源文件路径,复制源文件到指定目录


一、三种路径的分类:

1、绝对路径:

 绝对路径是从文件系统的根目录开始,完整地描述文件或目录位置的路径。

例如,在windows系统中,有个具体文件是 report.txt :

c:\User\user\docs\report.txt

2、相对路径:

 相对路径不包含完整的路径信息,而是根据当前工作目录来确定目标文件或目录的位置。

(  .  )  :表示当前目录。例如,.\example.txt 表示当前目录下的example.txt 文件。

(  ..  ) :表示上级目录。例如,..\Documents\example.txt 表示当前目录的上级目录下的Documents文件夹的 example.txt 文件

假如当前目录是home,..\..\表示的是当前home目录的上上级目录。

例子,假设当前的工作目录是 john,目标文件是c:\home\john\Documents\example.txt 。

绝对路径:c:\home\john\Documents\example.txt 。

相对路径:.\Documents\example.txt 。

在实际的开发中,绝对路径和相对路径用到的会更多。(尤其是相对路径,用的会更多)。

3、基准目录:

基准路径也就是当前工作目录。

基准路径 + 相对路径 = 绝对路径。

        基准路径是指当前程序或者操作所基于的目录。在进行文件操作时,相对路径的计算就以这个基准路径为起点。例如,若基准路径是c:\User\john,相对路径Documents\example.txt (省略了前面的  .\  )就表示的绝对路径是 c:\User\john\Documents\example.txt 。

二、文件的种类:

文件分为两大类:文本文件 和 二进制文件 

判断当前文件是文本文件还是二进制文件,初步手段是把文件拖入记事本里,如果是乱码就是二进制文件。反之是文本文件。(此方法有局限性)。

特性 文本文件 二进制文件
底层存储 由可读字符组成,使用字符编码(如 ASCII、UTF-8) 由任意二进制数据组成(0 和 1 的序列),无需遵循字符编码规则。
人类可读性 可直接用文本编辑器查看和编辑(如记事本)。 无法直接阅读(显示为乱码),需专用程序解析。
文件示例 .txt.csv.html.py .exe.jpg.mp3.docx

三、利用JAVA操作文件:

在JAVA中,我们使用 File 类和进行文件的操作:

        File类主要作用于文件或目录,它不仅可以用于创建文件和目录,还能进行一些与文件和目录相关的操作,比如获取文件或目录的属性、删除文件或目录等。

1、File类的构造方法:

注意,由于 .\ 会发生转移字符的情况,所以使用  ./  也可以(windows支持)。

有两种方法构造 File 类实例比较常用:

第一种(     File(String pathname)   ):

        通过指定的路径名字符串创建一个 File 实例。这个路径名可以是绝对路径,也可以是相对路径

 // 使用绝对路径创建 File 对象
    File absoluteFile = new File("C:/Users/example.txt");
        
// 使用相对路径创建 File 对象
    File relativeFile = new File("./test.txt");

 absoluteFile 依据绝对路径构建,而 relativeFile 则是基于相对路径构建,相对路径是相对于当前工作目录而言的。

第二种(   File(String parent , String child )   ):

        根据父路径名字符串和子路径名字符串创建一个 File 实例。系统会将父路径子路径拼接起来,形成完整的路径。

 String parent = "C:/Users";
 String child = "./example.txt";
 File file = new File(parent, child);

        parent 代表父路径,child 代表子路径,二者组合后生成完整路径 c:/Users/example.txt 。

2、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(File dest) 进⾏⽂件改名,也可以视为我们平时的剪切、粘贴操作
boolean canRead() 判断⽤⼾是否对⽂件有可读权限
boolean canWrite() 判断⽤⼾是否对⽂件有可写权限

使用例子

我的 java 工程目录:

 例子1:

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

        System.out.println(file.getParent());
        System.out.println(file.getName());
        System.out.println(file.getPath());
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getCanonicalPath());

        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        System.out.println(file.isFile());

代码运行结果(结合上面表格方法介绍看):

 例子2:

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

        // 创建文件,成功创建返回true,若存在返回false
        file.createNewFile();

        // 进程退出之后(代码运行结束), 才触发删除.
        file.deleteOnExit();

        Scanner scanner = new Scanner(System.in);
        System.out.println("输入任意内容, 退出程序");
        scanner.next(); // 阻塞

 代码运行结果(结合上面表格方法介绍看):

再看我们当前的工作目录:(test.txt文件已被删除)

例子3:

File file = new File("./");
        System.out.println(Arrays.toString(file.list()));
        System.out.println(Arrays.toString(file.listFiles()));

 代码运行结果(结合上面表格方法介绍看):

例子4:

  File file = new File("./test/aaa/bbb");

        // 创建一级目录
        System.out.println(file.mkdir());  // false

    ./test/aaa/bbb  是指定的路径。其中 ./ 代表当前目录,整体意思是要在当前目录下的 test 目录中的 aaa 目录里创建 bbb 目录。

        如果当前目录下已经有 test 目录,并且 test 目录下有 aaa 目录,而 aaa 目录中没有 bbb 目录,那么 mkdir() 方法会成功创建 bbb 目录,并且返回 true

        要注意的是,如果当前目录下没有test 目录,或者 test 目录下没有aaa目录,mkdir() 方法无法创建父目录,也就不能创建 bbb 目录,此时会返回 false。(因为我的工作目录没有当前的test目录)。

例子5:

  File file = new File("./test/aaa/bbb");
        
        // 创建多级目录
        file.mkdirs();

         mkdirs()这个方法,它会检查路径中各级目录是否存在,若不存在就会依次创建这些目录。例如,如果 test 目录不存在,它会先创建 test 目录;接着检查 aaa 目录,若不存在就创建 aaa 目录;最后检查并创建 bbb 目录。

例子6:

我的工程目录:

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

        File file2 = new File("./hello.txt");
        file.renameTo(file2);

 

此时test目录下的test.txt文件已经没了。

这里实现了两个操作。

        一是把 当前工作目录下的 test 目录 里的test.txt文件名字改为hello.txt,二是把名字改成 hello.txt 的文件移动到当前工作目录下。

        但是如果当前目录存在hello.txt文件,调用 file.renameTo(file2) 方法进行重命名和移动操作时会失败,renameTo() 方法会返回 false

        

四、利用JAVA操作文件内容:

       在 Java 里,操作文件内容时,有字节流字符流两种不同的输入输出方式。

字节流

        以字节(Byte)作为处理单位。一个字节为 8 位(bit),可处理任意类型的文件,像图片、音频、视频等二进制文件,也能处理文本文件。

字符流

        以字符(Character)作为处理单位。在 Java 中,一个字符通常是 16 位(2 字节),采用 Unicode 编码,适合处理文本文件,能直接处理字符数据,避免了字节流处理文本时的编码转换问题。

1、字节流:

读取操作:

        使用 InputStream 类来读取,但时,InputStream 是一个抽象类,它是所有字节输入流类的超类(超类是一种位于继承层次结构较高位置的类,其他类可以从它继承属性和方法),不能直接实例化,通常需要使用它的具体子类来完成实例化

实例化对象:

我们使用 FileInputStream 这个类来创建实例对象:

第一种:(    FileInputStream( String name )    )

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

        在上述代码中,使用 FileInputStream("./test.txt") 创建了一个输入流对象,用于读取当前目录下的 test.txt 文件。如果文件不存在,会抛出 FileNotFoundException 异常。

第二种:(     FileInputStream( File file )     )

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

        此代码先创建了一个 File 对象,然后使用 FileInputStream(file) 创建输入流对象。用于读取当前目录下的 test.txt 文件。如果文件不存在,会抛出 FileNotFoundException 异常。

 FileInputStream 核心方法:
方法 说明
int read( ) 此方法从输入流一次读取读取一个字节的数据,并将其作为一个 0 到 255 之间的整数返回。若到达输入流的末尾,返回 -1
int read(byte[ ] b) 尝试从输入流中读取最多 b.length 个字节的数据,并将其存储在字节数组 b 中。返回实际读取的字节数若到达输入流的末尾,返回 -1
int read(byte[ ] b, int off, int len) 从输入流中读取最多 len 个字节的数据,并将其存储在字节数组 b 中,从索引 off 开始存储。返回实际读取的字节数。若到达输入流的末尾,返回 -1

使用例子1:

InputStream inputStream = new FileInputStream("./test.txt");
       int data;

       while ((data = inputStream.read()) != -1) {
           System.out.print((char) data);
       }

  // 读取完毕, 循环 break 之后, 需要关闭文件.
     inputStream.close();

        上述代码中,(data = inputStream.read()) 这个表达式的结果就是赋给 data 的值,通过 while 循环不断调用 read() 方法读取文件内容,直到返回 -1 表示文件读取完毕。每次读取的字节数据被转换为字符并输出。

使用例子2:

InputStream inputStream = new FileInputStream("./test.txt");
        while (true) {
            byte[] bytes = new byte[1024];
            int n = inputStream.read(bytes);
            if (n == -1) {
                // 读取完毕了.
                break;
            }

            // 处理读取到的数据
            for (int i = 0; i < n; i++) {
                System.out.print((char)bytes[i]);
            }
        }
        // 读取完毕, 循环 break 之后, 需要关闭文件.
        inputStream.close();

        当文件剩余字节数大于字节数组长度,read方法就会尝试读取 bytes.length 个字节到 bytes 数组中,并返回 bytes.length 个字节个数给到 n 。

        当文件剩余字节数小于或等于字节数组长度,返回当前的有效字节的个数,再下一次循环就返回-1。

使用例子3:

 InputStream inputStream = new FileInputStream("./test.txt");
            byte[] buffer = new byte[1024];
            int offset = 0;
            int length = 512;
            int bytesRead;

            while(true) {
                bytesRead = inputStream.read(buffer, offset, length);

                if(bytesRead == -1) {
                    //读取完毕了.
                    break;
                }
                // 处理读取到的数据
                for (int i = offset; i < offset + bytesRead; i++) {
                    System.out.print((char) buffer[i]);
                }

                //下一次获取文件字节起始位置
                offset += bytesRead;
            }
        // 读取完毕, 循环 break 之后, 需要关闭文件.
        inputStream.close();

        此read方法从文件中最多读取len个字节的数据,从索引 offset 处开始存储,同理,与上述的例子2一样,读取到文件,末尾返回-1。

输出操作:

  使用 OutputStream 类来输出,但时,OutputStream 是一个抽象类,不能直接实例化,通常需要使用它的具体子类来完成实例化

实例化对象 

       我们使用 FileOutputStream 这个类来创建实例对象。

第一种:(    FileOutputStream(Sting name)   

 OutputStream fos = new FileOutputStream("./test.txt");

name 就是要打开的具体文件路径和文件名称。

第二种:(    FileOutputStream(Sting name , boolean append)   

OutputStream fos = new FileOutputStream("./test.txt", true);

        name 就是要打开的具体文件路径和文件名称。

        append 如果为true,则将字节数据从文件末尾处开始写入;如果为false,则会覆盖原文件内容。

 FileOutputStream 核心方法:
方法 解释
write ( int a) 写⼊要给字节的数据
write(byte[ ] b)  将字符数组 b 中的数据全部写入文件中
write(byte[ ] c , int off, int len) 将 c 这个字符数组中从 off 开始的 数据写⼊文件 中,⼀共写 len 个
flush() 我们知道 I/O (可以理解为从磁盘读取数据和把数据写入磁盘)的速度是很慢的,所以,⼤多的 OutputStream 为了减少设备操作的次数,在写数 据的时候都会将数据先暂时写⼊内 存的⼀个指定区域⾥,直到该区域 满了或者其他指定条件时才真正将 数据写⼊设备中,这个区域⼀般称 为缓冲区。但造成⼀个结果,就是 我们写的数据,很可能会遗留⼀部 分在缓冲区中。需要在最后或者合适的位置,调⽤ flush(刷新)操 作,将数据刷到设备中。

使用例子:

OutputStream fos = new FileOutputStream("./test.txt");

      int a = 'A';
      fos.write(a);

      byte[] arr = {'a',68,69};
      fos.write(arr);

      //刷新缓冲区
      fos.flush();

写入后,对应文件的内容。

2、字符流:

读取操作:

基于 Reader 这个抽象类来进行字符流读取,不能直接实例化,通常需要使用它的具体子类来完成实例化

实例化对象:

使用 FileReader 这个子类来进行实例化:

第一种:(   FileReader(String  name)   

Reader reader = new FileReader("./test.txt")

name 为具体的文件路径和文件名称。

第二种:(   FileReader(File file)   

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

        指定的 File 对象创建一个新的 FileReader 对象。若指定的 File 对象表示的文件不存在、是一个目录而非普通文件,或者由于其他原因无法打开该文件,会抛出 FileNotFoundException 异常。

 FileReader 核心方法:
方法 介绍
int read( )

读取单个字符,返回一个 0 到 65535 之间的整数,表示读取的字符。

如果已经到达流的末尾,则返回 -1。

int read(char[ ] arr )

将字符读入数组,返回读取的字符数。

如果已经到达流的末尾,则返回 -1。

int read(char[ ] arr , int off , int len)

将字符读入数组的一部分,数组从偏移量 off 开始存储字符,最多读取 len 个字符,

返回实际读取的字符数。

如果已经到达流的末尾,则返回 -1。

使用例子1:

文件含有内容:

Reader reader = new FileReader("./test.txt");

        while (true) {
             int ch = reader.read();
             if (ch == -1) {
                 break;
             }
             char c = (char) ch;
             System.out.println(c);

        }

运行结果:

使用例子2:

文件含有内容:

 Reader reader = new FileReader("./test.txt");

        while (true) {

            char[] chars = new char[1024];
            int n = reader.read(chars);
            if (n == -1) {
                break;
            }
            for (int i = 0; i < n; i++) {
                System.out.println(chars[i]);
            }
        }

运行结果:

输出操作:

使用 Writer 类来输出,因为它是抽象类,所以不能直接实例化,通常要使用它的具体子类来完成实际的字符写入操作。

实例化对象:

使用 FileWriter 这个子类来实例化对象。

第一种:(   FileWriter(String  name)   

Writer writer = new FileWriter("./test.txt");

name 就是要打开的具体文件路径和文件名称。

第二种:(   FileWriter(File file ,boolean append)   

Writer writer = new FileWriter("./test.txt", true);

        name 就是要打开的具体文件路径和文件名称。

        append 如果为true,则将字节数据从文件末尾处开始写入;如果为false,则会覆盖原文件内容。

 FileWriter 核心方法:
方法 解释
write( ) 写入单个字符,或者字符串
write(char[ ] arr ) 将字符数组中的所有字符写入文件。
write(char[ ] arr ,int off , int len) 将字符数组的 off 位置开始写入,最多写入 len 个
flush( ) 刷新缓冲区

使用例子:

Writer writer = new FileWriter("./test.txt");

        writer.write("你好世界");
        char[] arr = {'z','k'};
        writer.write(arr);
        
        writer.flush();

代码运行结果:

五、使用文件操作的案例:

1、根据用户输入的目录和文件名,来进行文件的查找和删除

public static void main(String[] args) {
        //用户输入要查询的文件名
        Scanner scanner = new Scanner(System.in);
        System.out.println("温馨提示,输入的目录要符合d:/ 或者 d:/User/...的格式");
        System.out.println("请输入要查询的目录: ");
        String dir = scanner.nextLine();
        System.out.println("请输入要查询的文件名: ");
        String filename = scanner.nextLine();

        //判断目录是否存在
        File rootFile = new File(dir);
        if(!rootFile.isDirectory()) {
            System.out.println("目录不存在!");
            return;
        }

        //进行搜索,递归遍历目录中所有的文件和子目录
        scanDir(rootFile,filename);
    }

    private static void scanDir(File rootFile, String filename) {
        //列出 rootFile 中的内容
        File[] files = rootFile.listFiles();
        if(files == null) {
            //空目录
            return;
        }

        //遍历 files 数组,判断每个元素的类型
        for (File file : files) {
            if(file.isDirectory()) {
                //是一个目录,递归调用 scanDir 方法.
                scanDir(file,filename);
            }else if(file.isFile()) {
                //是一个文件,判断文件名是否匹配。
                if(file.getName().contains(filename)) {
                    tryDelete(file);
                }
            }
        }
    }

    private static void tryDelete(File file) {
        System.out.println("已找到改文件,文件路径为:" + file.getAbsolutePath());
        System.out.println("是否删除该文件(Y/N)");
        Scanner scanner = new Scanner(System.in);
        String choice = scanner.next();
        if(choice.equals("Y") || choice.equals("y")) {
            file.delete();
            System.out.println("删除成功!");
        }
    }

进阶版:

public class Demo25 {
    //记录文件是否存在
    public static boolean count = true;

    public static void main(String[] args) {
          while(true) {
              // 每次查找前重置 count 变量
              count = true;
              //用户输入要查询的文件名
              Scanner scanner = new Scanner(System.in);
              System.out.println("此程序用来进行文件的查找和删除");
              System.out.println("温馨提示,输入的目录要符合d:/ 或者 d:/User/...的格式");
              System.out.println("请输入要查询的目录: ");
              String dir = scanner.next();
              scanner.nextLine();
              System.out.println("请输入要查询的文件名(文件名可以是部分名称): ");
              String filename = scanner.next();
              scanner.nextLine();

              //判断目录是否存在
              File rootFile = new File(dir);
              if(!rootFile.isDirectory()) {
                  System.out.println("目录不存在!");
                  System.out.println();
                  continue;
              }

              //进行搜索,递归遍历目录中所有的文件和子目录
              scanDir(rootFile,filename);

              if(count == true) {
                  System.out.println("文件不存在,或者文件名输入错误!");
              }

              System.out.println("输入 1 继续查找,输入 0 退出程序");

              int a = 0;
              while(true) {
                  if(scanner.hasNextInt()) {
                      a = scanner.nextInt();
                      if(a == 1) {
                          break;
                      }else if(a == 0) {
                          return;
                      }else {
                          System.out.println("输入的数字无效,请重新输入: ");
                          // 除去无效输入的内容
                          scanner.nextLine();
                      }
                  }else {
                      System.out.println("输入错误,请输入数字: ");
                      //除去无效输入的内容
                      scanner.nextLine();
                  }
              }
          }
       }

    private static void scanDir(File rootFile, String filename) {
        //列出 rootFile 中的内容
        File[] files = rootFile.listFiles();
        if(files == null) {
            //空目录
            return;
        }

        //遍历 files 数组,判断每个元素的类型
        for (File file : files) {
            if(file.isDirectory()) {
                //是一个目录,递归调用 scanDir 方法.
                scanDir(file,filename);
            }else if(file.isFile()) {
                //是一个文件,判断文件名是否匹配。
                if(file.getName().contains(filename)) {
                    tryDelete(file);
                    count = false;
                }
            }
        }
    }

    private static void tryDelete(File file) {
        System.out.println("已找到该文件,文件路径为:" + file.getAbsolutePath());
        System.out.println("是否删除该文件(Y/N)");
        Scanner scanner = new Scanner(System.in);
        String choice = scanner.next();
        if(choice.equals("Y") || choice.equals("y")) {
            file.delete();
            System.out.println("删除成功!");
        }
    }
}

2、基根据用户输入的源文件路径,复制源文件到指定目录

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;
        }

        // 因为destPath最后包含了文件名,而我们所需要的只是前面的目录
        File destFile = new File(destPath);
        //得到用户输入的目标目录的父目录
        File parentFile = destFile.getParentFile();
        //判断得到的目标目录的父目录计算机中是否存在
        if(!parentFile.isDirectory()) {
            System.out.println("目标父目录不存在");
            return;
        }

        //进行拷贝
        copy(srcFile,destFile);
    }

    private static void copy(File srcFile, File destFile) {
        try(InputStream inputStream = new FileInputStream(srcFile);
            OutputStream outputStream = new FileOutputStream(destFile)) {

            while(true) {
                byte[] arr = new byte[1024];
                int len = inputStream.read(arr);
                if(len == -1) {
                    break;
                }
                outputStream.write(arr,0,len);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }


网站公告

今日签到

点亮在社区的每一天
去签到