【Java IO流】File文件管理及IO流(基本流)

目录

File及IO概述

一、File-文件管理

1. File概念及构造方法

2. 常见的成员方法

3. 综合练习

二、IO流

1. IO流概述及体系

2. 字节输出流和字节输入流

3. 了解字符集及Java编码解码的方法

4. 字符输入流和字符输出流

5. 综合练习


File及IO概述

在电子设备的日常使用中,我们无时不刻在对各个应用的数据进行存储和读取,正因为有了这个技术,对我们的信息存储带来了极大便利。

在Java中,IO流就是用于读写文件中的数据(读写文件,或网络中的数据)。而IO流的使用,离不开文件操作,它们间存在密切的联系,通常一起用来实现文件的读取、写入和处理。

总之,文件管理提供了对文件系统的操作能力,而I/O流则提供了对文件内容的读取和写入能力,二者结合使用可以实现对文件的全面管理和处理。在Java中,java.iojava.io.file包提供了丰富的类和方法,支持文件管理和I/O流的操作。


一、File-文件管理

1. File概念及构造方法

重要概念:

路径(Path)是用于描述文件或文件夹在文件系统中位置的字符串。路径可以指定文件或文件夹的唯一位置,以便在计算机系统中进行访问和操作。

在计算机中,有两种常见的路径表示方式:

  1. 相对路径(Relative Path):相对路径是相对于当前工作目录(Working Directory)的路径。它描述了从当前位置到目标文件或文件夹的相对位置。相对路径通常使用一些特殊符号来表示位置关系,例如..表示上级目录,.表示当前目录。相对路径适用于在当前工作目录下进行文件操作。

  2. 绝对路径(Absolute Path):绝对路径是完整的路径,从根目录开始一直到目标文件或文件夹的路径。它描述了文件或文件夹在文件系统中的确切位置。绝对路径在不同的操作系统和文件系统中可能存在差异,例如Windows系统中的绝对路径以盘符开头(如C:\folder\file.txt),而Unix/Linux系统中的绝对路径以斜杠/开头(如/home/user/file.txt)。绝对路径适用于指定文件系统中任意位置的文件或文件夹。

在使用路径时,需要注意以下几点:

  • 在Windows系统中,路径分隔符为反斜杠\,而在Unix/Linux系统中,路径分隔符为正斜杠/
  • 对于包含空格或特殊字符的路径,可以使用引号或转义字符来处理。
  • 在编程语言中,通常会提供相关的API或函数来操作和处理路径,以简化路径的使用和转换。

构造方法:

这些构造方法允许我们根据不同的情况来创建File对象,可以根据路径字符串、父目录和子目录/文件名字符串、父目录File对象等来表示文件或目录。

在Java中,路径的分隔符反斜杠\是一个转义字符,所以在填写路径时,要用\\转义成一个\


2. 常见的成员方法

(1)判断、获取:

(2)创建、删除:

(3)获取并遍历


3. 综合练习

下面,通过一些练习介绍一些方法的细节。

需求:
     在当前模块下的aaa文件夹中创建一个a.txt文件
public class FileTest1 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
             在当前模块下的aaa文件夹中创建一个a.txt文件
        */
        //1.创建父级目录
        File file = new File(".\\aaa");
        file.mkdirs();
        //2.创建子级目录并拼接
        File src = new File(file, "a.txt");
        boolean b = src.createNewFile();
        if (b) {
            System.out.println("创建成功");
        } else {
            System.out.println("创建失败");
        }
    }
}

createNewFile(); 这个方法的细节:已有该文件时,返回false。

需求:
    定义一个方法找某一个文件夹中,是否有以avi结尾的电影。
    (暂时不需要考虑子文件夹)
public class FileTest2 {
    public static void main(String[] args) {
        /*
        需求:
            定义一个方法找某一个文件夹中,是否有以avi结尾的电影。
            (暂时不需要考虑子文件夹)
         */
        File file = new File("aaa");
        boolean b = FileTest2.haveAVI(file);
        System.out.println(b);
    }

    //找某一个文件夹中,是否有以avi结尾的电影
    public static boolean haveAVI(File file) {
        //1.获取所有文件夹
        File[] files = file.listFiles();
        //2.遍历
        if (files != null) {
            for (File f : files) {
                //f依次表示File里面的文件或者文件夹
                if (f.isFile() && f.getName().endsWith(".avi")) {
                    return true;
                }
            }
        }
        return false;
    }
}

listFiles(); 这个方法的细节,如果当前文件夹没有子文件,返回的File[]数组为null

需求:
    删除一个多级文件夹
public class FileTest4 {
    public static void main(String[] args) {
        /*
        需求:
            删除一个多级文件夹
         */
        File file = new File("aaa\\src");
        delete(file);
    }

    /*
        参数src表示要删除的文件夹
     */
    public static void delete(File src) {
        //1.获取所有文件夹
        File[] files = src.listFiles();
        //2.遍历
        if (files != null) {
            for (File file : files) {
                if (file.isFile()) {   //1.如果要删除的是文件,直接删除
                    file.delete();
                } else {    //2.如果要删除的是文件夹,继续执行该方法(递归)
                    delete(file);
                }
            }
        }
        //3.删除自己
        src.delete();
    }
}

 delete(); 这个方法的返回值和createNewFile()很相似,没有该文件时会返回false

 如果要删除的是文件夹,需要删除该文件夹中的所有文件。


二、IO流

1. IO流概述及体系

IO流就是用于读写文件中的数据(读写文件,或网络中的数据)。IO流又分基本流高级流两种类型。高级流又是基于基本流之上的一层封装,提供了更丰富、更方便的功能。高级流通常用于处理特定格式的数据、实现数据缓冲、提供对象序列化等高级操作。

常见的高级流有:缓冲流、对象流、转换流、数据流、打印流、压缩流

这次我们主要介绍基本流:

 流的方向:                                                                               

 

操作文件类型:

体系:


2. 字节输出流和字节输入流

FileOutputStream:操作本地文件的字节输出流,可以把程序中的数组写到本地文件中。

三个主要部分:

1.字节输出流的三大步骤

2.字节输出流写出文件的三种方式

3.换行(\r\n)和续写

public class FileOutput {   //字节输出流
    public static void main(String[] args) throws IOException {
        //1.创建对象 实参是文件路径(没有则创建 有则清空原文件)
        FileOutputStream fos = new FileOutputStream("File\\aaa");
        //2.写出数据
        fos.write(97);
        //3.释放资源
        fos.close();

        /*
            字节输出流写出文件的三种方式
         */
        FileOutputStream fos1 = new FileOutputStream("File\\bbb");
        //1.单字符写入
        fos1.write(97);
        //2.byte数组写入
        byte[] bytes = {97, 98, 99, 100, 101};
        fos1.write(bytes);
        //3.byte数组指定位置和长度写入
        fos1.write(bytes, 1, 2);
        fos1.close();

        /*
            换行和续写
         */
        FileOutputStream fos2 = new FileOutputStream("File\\ccc");
        //换行
        String str = "woleigedou";
        byte[] bs = str.getBytes();
        fos2.write(bs);
        fos2.write("\r\n".getBytes()); //换行
        fos2.write("666".getBytes());

        fos2.close();

        //续写
        FileOutputStream fos3 = new FileOutputStream("File\\aaa", true);
        fos3.write("xuxie\r\n".getBytes());
        fos3.write("666".getBytes());

        fos3.close();
    }
}

FileInputStream:操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来。

注意字节输入流读取数据的细节:

public class FileInput {    //字节输入流
    public static void main(String[] args) throws IOException {
        //读取数据
        //如果文件不存在,直接报错
        FileInputStream fis = new FileInputStream("File\\bbb");
        int b1 = fis.read();
        System.out.println((char) b1);
        int b2 = fis.read();
        System.out.println((char) b2);
        int b3 = fis.read();
        System.out.println((char) b3);
        fis.close();

        System.out.println("===============");

        //循环读取
        FileInputStream fis1 = new FileInputStream("File\\bbb");
        //read方法,一次读取一个字节,并移动指针
        //文件数据:aabcdebc
        //错误示例:
        while (fis1.read() != -1) {
            System.out.print((char) fis1.read());
        }
        fis1.close();

        System.out.println();
        System.out.println("===============");
        //文件数据:aabcdebc
        //正确示例:
        FileInputStream fis2 = new FileInputStream("File\\bbb");
        int b;
        while ((b = fis2.read()) != -1) {
            System.out.print((char) b);
        }
        fis2.close();
    }
}

 字节输入流每调用一次read()方法,文件中的指针就会移动一次,因此在错误示例中,会导致读一个字节漏一个字节。

 知道字节输入流读取文件的方式后,我们就可以读取文件的数据,并且结合字节输出流边读边写完成文件拷贝的操作。

而读一个字节写一个字节的效率非常低,因此通常一次读取一个字节数组的内容并一次性写出,大大提高了读写效率。

        int len;
        byte[] bytes = new byte[1024 * 500];  //这里是500KB 通常是1024的整数倍
        while ((len = fis.read(bytes)) != -1) {
            //写上面读到的数据长度
            fos.write(bytes, 0, len);
        }

3. 了解字符集及Java编码解码的方法

了解完字节流后,我们就可以想想为什么还要有字符流呢?

这时候就要知道,为什么会产生乱码?

乱码是因为字符编码和解码不一致,导致字符集无法正确转换所致。

在计算机中,字符在存储和传输时都需要进行编码和解码。字符编码是将字符映射为数字(二进制)的过程,而字符解码则是将数字(二进制)转换为字符的过程。在进行编码和解码时,如果使用不同的字符集或编码方式,就会导致字符集转换错误,从而产生乱码。

所以,为了能正确转换文本文件,就有了字符流。

字符流是对字节流的一层封装,它提供了一种更高级、更方便的方式来处理文本数据。在Java中,使用字符流可以方便地读写文本文件或网络数据,而不需要关心具体的字符编码和转换。

下面列举一些使用字符流的优点:

  1. 高效性:与字节流相比,字符流可以快速地处理大量的字符数据,因为它们以字符为单位进行读取和写入,避免了对单个字节进行频繁的读写操作。

  2. 地域性:不同的国家和地区使用不同的字符集和编码方式,字符流可以根据指定的字符编码自动进行字符集转换,从而确保读写操作的正确性和一致性。

  3. 处理文本数据:字节流只能读写二进制数据,无法直接处理文本数据。而字符流提供了处理文本数据的高级抽象,可以方便地读写文本文件、HTML页面等。

最常见的字符集有GBK,UTF-8,Unicode等,常用的IDEA编译器使用的就是UTF-8字符集,Eclipse使用的是GBK字符集。

如何不产生乱码?
    1. 不要使用字节流读取文本文件
    2. 编码解码时使用同一个码表,同一个编码方式

Java中编码的方法
    public byte[] getBytes()                    使用默认方式进行编码
    public byte[] getBytes(String charsetName)  使用指定方式进行编码

Java中解码的方法
    String(byte[] bytes)                        使用默认方式进行解码
    String(byte[] bytes, String charsetName)    使用指定方式进行解码
public class Test { //java中编码和解码的代码实现
    public static void main(String[] args) throws UnsupportedEncodingException {
        //1.编码
        String str = "奥里给!";
        byte[] bytes1 = str.getBytes(); //使用默认方式进行编码
        System.out.println(Arrays.toString(bytes1));

        byte[] bytes2 = str.getBytes("GBK"); //使用指定方式进行编码
        System.out.println(Arrays.toString(bytes2));

        //2.解码
        String str1 = new String(bytes1);   //使用默认方式进行解码
        System.out.println(str1);

        String str2 = new String(bytes2, "GBK");    //使用指定方式进行解码
        System.out.println(str2);

        //3.编码解码使用不同的编码方式
        String s = new String(bytes1, "GBK");
        System.out.println(s);

        String s1 = new String(bytes1, StandardCharsets.US_ASCII);
        System.out.println(s1);
    }
}

4. 字符输入流和字符输出流

字符输入流:
    字符流的底层也是字节流,默认也是一个字节一个字节进行读取的。
    如果遇到中文一次读取多个,GBK编码一次读取2个字节,UTF-8一次读取3个字节。
public class FileReaderDemo {   //字符输入流
    public static void main(String[] args) throws IOException {
        //1.空参read()方法 -- 返回一个十进制数字
        // public int read()  读取数据,读到末尾返回-1

        //1.1 创建对象
        FileReader fr = new FileReader("File\\test.txt");
        //1.2 读取数据
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.print((char) ch);
        }
        //1.3 释放资源
        fr.close();

        System.out.println();
        System.out.println("=======================");

        //2.带参read()方法 -- 读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
        // public int read(char[] buffer)  读取多个数据,读到末尾返回-1

        //2.1 创建对象
        FileReader fr1 = new FileReader("File\\test.txt");
        //2.2 读取数据
        char[] chars = new char[2];
        int len;
        while ((len = fr1.read(chars)) != -1) {
            //把数组中的数据变成字符串再进行打印
            System.out.print(new String(chars, 0, len));
        }
        //2.3 释放资源
        fr1.close();
    }
}
字符输出流:
        void write(int c)                           //写出一个字符
        void write(String str)                      //写出一个字符串
        void write(String str, int off, int len)    //写出一个字符串的一部分
        void write(char[] cbuf)                     //写出一个字符数组
        void write(char[] cbuf, int off, int len)   //写出一个字符数组的一部分
public class FileWriterDemo {   //字符输出流
    public static void main(String[] args) throws IOException {
        //1.创建对象
        FileWriter fw = new FileWriter("File\\test1.txt");

        //2.写入数据
        //fw.write("奥利奥");    //写出一个字符串
        char[] chars = {'6', '6', '6', '牛'};    //写出一个字符数组
        fw.write(chars);
        //续写 -- 构造方法中打开续写开关
        //FileWriter fw = new FileWriter("File\\test1.txt", true);

        //3.释放资源
        fw.close();
    }
}

5. 综合练习

深入学习字节流和字符流后,就可以解决一些不太复杂的问题了。

拷贝文件夹
    需求:
        拷贝一个文件夹,考虑子文件夹。
public class Test1 {
    public static void main(String[] args) throws IOException {
        /*
        拷贝文件夹
            需求:
                拷贝一个文件夹,考虑子文件夹
         */

        //1.创建对象表示数据源
        File src = new File("File\\src");
        //2.创建对象表示目的地
        File dest = new File("File\\dest");

        //3.调用方法开始拷贝
        copydir(src, dest);
    }

    private static void copydir(File src, File dest) throws IOException {
        dest.mkdirs();
        //1.进入数据源
        File[] files = src.listFiles();
        //2.遍历数组
        for (File file : files) {
            if (file.isFile()) {
                //3.文件 拷贝
                FileInputStream fis = new FileInputStream(file); //要拷贝的文件
                FileOutputStream fos = new FileOutputStream(new File(dest, file.getName()));
                byte[] bytes = new byte[1024];
                int len;
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fos.close();
                fis.close();
            } else {
                //4.文件夹 递归
                copydir(file, new File(dest, file.getName()));
            }
        }
    }
}
修改文件中的数据
    文本文件中有以下的数据:
            2-1-9-4-7-8
    将文件中的数组进行排序,变成以下的数据:
            1-2-4-7-8-9
public class Test3_1 {
    public static void main(String[] args) throws IOException {
        //1.读取数据
        FileReader fr = new FileReader("File\\a.txt");
        StringBuilder sb = new StringBuilder();
        int ch;
        while ((ch = fr.read()) != -1) {
            sb.append((char) ch);
        }
        fr.close();
        //2.排序
        String str = sb.toString();
        String[] split = str.split("-");

        ArrayList<Integer> list = new ArrayList<>();    //将数据转换成整型
        for (String s : split) {
            list.add(Integer.parseInt(s));
        }
        System.out.println(list);

        Collections.sort(list);
        //3.写出数据
        FileWriter fw = new FileWriter("File\\a.txt");
        for (int i = 0; i < list.size(); i++) {
            if (i == list.size() - 1) {
                fw.write(list.get(i) + "");    //将list转成字符写入
            } else {
                fw.write(list.get(i) + "-");
            }
        }
        fw.close();
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值