流对象操作文件内容

目录

Reader

Writer 

小程序1

小程序2


针对文件内容,使用“流对象”进行操作

Java标准库的流对象,从类型上分为两个大类:

1.字节流——操作二进制数据的

InputStream——FileInputStream

OutputStream——FileOutStream

2.字符流——操作文本数据的 

Reader——FileReader

Writer——FileWriter

这些类的使用方式是非常固定的,核心就是四个操作:

1)打开文件(构造对象)

2)关闭文件(close)

3)读文件(read)——针对InputStream和Reader

4)写文件(write)——针对OutputStream和Writer

我们可以看到,read有三个版本:

1)read无参数版本:一次读一个字节

2)read一个参数版本:把读到的内容填充到参数的字节数组里(此处的参数是个“输出型参数”),返回值是实际读取的字节数。

3)read三个参数版本:和2类似,只不过是往数组的一部分区间里尽可能填充

下面我们用字节流来读取文件:

1)read无参数版本:一次读一个字节

//使用字节流来读取文件
public class IODemo3 {
    public static void main(String[] args) throws IOException {
        //创建InputStream对象的时候,使用绝对路径或者相对路径,都是可以的,也可以使用File对象。

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

        //进行读操作
        while (true) {
            int b = inputStream.read();
            if (b == -1) {
                //读取完毕
                break;
            }
            System.out.println("" + (byte) b);
        }

        inputStream.close();//关闭
    }
}

这些数字,就是hello的Ascii码 

2)read一个参数版本:

while (true) {
            byte[] buffer = new byte[1024];//提前准备好一个数组
            int len = inputStream.read(buffer);
            //这里的传参操作,是把准备好的数组,交给read方法,让read方法内部针对这个数组进行填写(此处参数相当于“输出型参数”)
            System.out.println("len:" + len);
            if (len == -1) {
                break;
            }
            //此时读取的结果就被放到 byte 数组中
            for (int i = 0; i < len; i++) {
                System.out.printf("%x\n",buffer[i]);
            }
        }
        inputStream.close();//关闭
    }

 

 

我们读一个大一点的文件观察:


上面我们使用了InputStream来读文件,我们还可以使用OutputStream来写文件:

public class IODemo4 {
    //进行写文件
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("d:/test.txt");

        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);
        outputStream.write(100);

        outputStream.close();
    }
}

同样的,write也有三种用法

大家有没有发现一个问题,我们每次结束都要调用close()操作呢,这是为什么呢?

 当然一般写代码,还是要注意这个close,,那么如何才能确保这个close被执行到呢?这里有一个好的写法:

public static void main(String[] args) throws IOException {
        try (OutputStream outputStream = new FileOutputStream("d:/test.txt")){
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
        }
    }

这个写法虽然没有显示的写close,但只要try语句块执行完毕,就可以自动执行到close!

这个语句,在Java中被称为 try with resources,那我们是不是随便拿个对象放到try()里就能够自动释放呢?——得满足一定条件!


很明显,这里的txt是文本文件,使用字节流可以读,但是不方便,我们更希望使用的是一个个字符表示,所以使用字符流,会更方便:

Reader

 public static void main1(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();
        }
    }

Writer 

public static void main(String[] args) {
        try (Writer writer = new FileWriter("d:/test.txt")){
            writer.write(("hello world"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 像这样的写操作,其实是先写到缓冲区里(缓冲区存在很多种形态,咱们自己的代码里可以有缓冲区;标准库里也可以有缓冲区;操作系统内核里也可以有缓冲区.....)

写操作执行完了,内容可能在缓冲区里,还没有真的进入硬盘。

close操作,就会触发缓冲区的刷新(刷新操作,就是把缓冲区里的内容写到硬盘)

除了close之外,还可以通过flush方法,也能起到刷新缓冲区的效果。

public static void main(String[] args) {
        try (Writer writer = new FileWriter("d:/test.txt")) {
            writer.write(("hello world"));
            //手动刷新缓冲区
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

其他流对象:Scanner

public static void main(String[] args) {
        //Scanner scanner = new Scanner(System.in);//System.in其实就是一个流对象
        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
            Scanner scanner = new Scanner(inputStream);//Scanner的close本质上是要关闭内部包含的这个流对象
            //此时,内部的inputStream对象已经被try()关闭了,里面的这个Scanner不关闭也没事

            //此时读取的内容就是从 文件 进行读取了
            scanner.next();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

下面我们写几个小程序来练习一下文件的操作

小程序1

扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
public class IODemo7 {
    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("请输入要删除的文件名:");
        //此处要使用next,不能使用nextLine()!!!
        String nameToDelete = scanner.next();

        //针对指定的路径进行扫描,递归操作
        //先从根目录出发(root)
        //先判定一下,当前这个根目录里,是否包含要删除的文件,如果是,就删除;否则就跳过下一个
        //如果当前包含了一些目录,针对子目录再进行递归

        scanDir(root, nameToDelete);

    }

    private static void scanDir(File root, String nameToDelete) {
        System.out.println("[scanDir]" + root.getAbsolutePath());
        //1.先列出 root 下的文件和目录
        File[] files = root.listFiles();
        if (files == null) {
            //当前 root 目录下没东西,是一个空目录
            //结束继续递归
            return;
        }
        //2.遍历当前的列出结果
        for (File f : files) {
            if (f.isDirectory()) {
                //如果还是目录,进一步递归
                scanDir(f, nameToDelete);
            } else {
                //如果是普通文件,判定是否要删除
                if (f.getName().contains(nameToDelete)) {
                    System.out.println("确认是否要删除" + f.getAbsolutePath() + "?");
                    String choice = scanner.next();
                    if (choice.equals("y") || choice.equals("Y")) {
                        f.delete();//包含就删除
                        System.out.println("删除成功!");
                    } else {
                        System.out.println("取消删除!");
                    }

                }
            }

        }
    }
}

 

小程序2

进行普通文件的复制
把一个文件拷贝成另一个文件,就是把第一个文件按照字节依次读取,把结果写入到另一个文件中。
public class IODemo8 {
    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()语法,支持包含多个流对象。多个流对象之间,使用;分隔开
        try (InputStream inputStream = new FileInputStream(srcFile);
             OutputStream outputStream = new FileOutputStream(destFile)) {

            //进行读文件操作
            while (true) {
                int b = inputStream.read();
                if (b == -1) {
                    break;
                }
                outputStream.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
这里的代码只能拷贝一个文件,如果是要拷贝目录,就需要像上个例子一样,递归的拷贝
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值