JavaSE——IO流2:FileReader(字符输入流)、FileWriter(字符输出流)

一、字符流概述

字符流的底层就是字节流+字符集。

特点:

        输入流:一次读取一个字节,遇到中文时,一次读取多个字节。

        输出流:底层会把数据按照指定的编码方式进行编码,编程字节再写到文件中。

使用场景:对于纯文本文件进行读写操作。

二、字符输入流与字符输出流

        字符流在底层实现中已经有了一定的缓冲机制(通过字节缓冲区实现字符的读写),但用户通常不会直接操作这个缓冲区。此外,Java还提供了BufferedReader和BufferedWriter等缓冲字符流来进一步提高效率。

(一)字符输入流——FileReader

1.FileReader书写步骤

(1)创建字符输入流对象

(2)读取数据

(3)释放资源

2.FileReader书写细节

(1)创建字符输入流对象

构造方法说明
public FileReader(File file)创建字符输入流关联本地文件
public FileReader(String pathname)创建字符输入流关联本地文件

(2)读取数据

成员方法说明
public int read()读取数据,读到末尾返回-1
public int read(char[ buffer])读取多个数据,读到末尾返回-1

细节1:按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个十进制整数

细节2:读到文件末尾了,read方法返回-1

(3)释放资源

成员方法说明
public int close()释放资源/关流

3.空参的read方法读取数据

public class CharStreamDemo1 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("chapter18\\d.txt");
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.print((char) ch);
        }
        fr.close();
    }
}

运行结果:

4.有参的read方法读取数据

read(chars):读取数据、解码、强转三步合并了,把强转之后的字符放到数组当中

                      相当于read()+强制类型转换

public class CharStreamDemo2 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("chapter18\\d.txt");

        int len;
        char[] chars = new char[2];
        while ((len = fr.read(chars)) != -1) {
            // 把数组中的数据变成字符串再进行打印
            System.out.print(new String(chars, 0, len));
        }
        fr.close();
    }
}

运行结果:

5.FileReader底层原理

同时读写一个文件:

(二)字符输出流——FileWriter

1.FileWriter方法介绍

(1)FileWriter构造方法
构造方法说明
public FileWriter(File file)创建字符输出流关联本地文件
public FileWriter(String pathname)创建字符输出流关联本地文件
public FileWriter(File file, boolean append)创建字符输出流关联本地文件,续写
public FileWriter(String pathname, boolean append)创建字符输出流关联本地文件,续写
(2)FileWriter成员方法
成员方法说明
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)写出字符数组的一部分

2.FileWriter书写细节

3.FileWriter写出数据

关于字节输入流写出数据的弊端:

        一个字节(Byte)是计算机存储数据的基本单位,它固定等于8位(Bits)。因此,一个字节的最大值是255(十进制),或者说是`0xFF`(十六进制),或者说是`11111111`(二进制)。

        如果你有一个字节输出流(例如,在Java中的`OutputStream`),并且你希望每次只能操作一个字节,那么你需要确保每次写入的整数不超过一个字节的范围,即0到255之间(包括0和255)。

        字节输出流一次性写出大小超过一个字节大小代码示例:

public class CharStreamDemo3 {
    public static void main(String[] args) throws IOException {
       // 字节输出流写出数据,每次只能操作一个字节,传入的参数不能超过一个字节的大小
        FileOutputStream fos = new FileOutputStream("chapter18\\e.txt");
        fos.write(25105);
        fos.close();
    }
}

        运行结果:

字符输出流写出数据:

public class CharStreamDemo3 {
    public static void main(String[] args) throws IOException {
        // 字符输出流写出数据                    打开续写开关
        FileWriter fw = new FileWriter("chapter18\\e.txt", true);
        // 根据字符集的编码方式进行编码,把编码之后的数据写到文件中去
        // IDEA默认是UTF-8

        // 写出一个字符
        fw.write(25105);

        // 写出一个字符串
        String str = "\n向晚意不适,驱车登古原。\n夕阳无限好,只是近黄昏。\n";
        fw.write(str);

        // 写出一个字符串的一部分
        fw.write(str, 7, 5);

        // 写出一个字符数组
        char[] chars = {'a', 'b', 'c', 'd'};
        fw.write(chars);

        fw.close();
    }
}

运行结果:

4.FileWriter底层原理——flush和close方法

成员方法说明
public void flush()

将缓冲区的数据,刷新到本地文件中

刷新之后,还可以继续往文件中写出数据

public void close()

释放资源/关流

断开通道,无法再往文件中写出数据

将缓冲区的数据写入到文件中有三种情况:

  1. 缓冲区装满了(长度为8192的字节数组)
  2. 使用flush方法,会将缓冲区的数据写出到文件中,后面还可以继续往文件中写出数据
  3. 使用close方法,关流,通道关闭,后面的数据就不会再写出到文件中,抛出IOException异常

使用flush方法,如果没有关流,那么flush后面的数据不会写出到文件中,是将缓冲区的数据写出到文件中了。

如果有关流,那么后面的数据就会重新加载到缓冲区最前面,写出到文件中。

加载前:

加载后:

三、字节流与字符流综合练习(重要)

字节流:拷贝任意类型的文件

字符流:读取纯文本文件中的数据;往纯文本文件中写出数据

1.拷贝一个文件夹,考虑子文件夹

public class TestDemo1 {
    public static void main(String[] args) throws IOException {
        File src = new File("D:\\test");
        File dest = new File("chapter18\\copytest1");

        copyDir(src, dest);
    }

    private static void copyDir(File src, File dest) throws IOException {
        // 创建要拷贝的文件夹
        dest.mkdirs();

        // 1.进入数据源
        File[] files = src.listFiles();
        // 如果进入的文件夹没有权限,就需要做一个非空判断
        if (files != null) {
            // 2.遍历数组
            for (File file : files) {
                if (file.isFile()) {
                    // 3.判断是文件,字节流进行拷贝
                    FileInputStream fis = new FileInputStream(file);
                    FileOutputStream fos = new FileOutputStream(new File(dest, file.getName()));
                    int len;
                    byte[] bytes = new byte[1024 * 1024];
                    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.文件加密

为了保证文件的安全性,就需要对原始文件进行加密存储,再次使用的时候再对其进行解密处理。

加密原理:对原始文件中的每一个字节数据进行更改,然后将更改以后得数据存储到新的文件中。

解密原理:读取加密之后的文件,按照加密的规则反向操作,变成原始文件。

 ^ : 异或
                 两边相同:false
                 两边不同:true

                 0:false
                 1:true

               100:1100100
               10: 1010

               1100100
             ^ 0001010
             __________
               1101110
             ^ 0001010
             __________
               1100100

一个数异或自己两次还是自己

public class TestDemo1 {
    public static void main(String[] args) throws IOException {
        // 给文件加密操作
//        File src = new File("chapter18\\图像1.jpg");
//        File dest = new File("chapter18\\ency.png");
//        ency(src, dest);

        // 给文件解密操作
        File encyFile = new File("chapter18\\ency.png");
        File redoFile = new File("chapter18\\redo.png");
        ency(encyFile,redoFile);
    }

    private static void ency(File src, File dest) throws IOException {
        // 1.创建对象关联原始文件
        FileInputStream fis = new FileInputStream(src);
        // 2.创建对象关联加密/解密后的文件
        FileOutputStream fos = new FileOutputStream(dest);
        // 3.加密处理
        int len;
        while ((len = fis.read()) != -1) {
            fos.write(len ^ 10);
        }
        // 4.释放资源
        fos.close();
        fis.close();
    }
}

3.修改文件中的数据

将f.txt文件中的2-1-9-4-7-8按照升序排序

public class TestDemo3 {
    public static void main(String[] args) throws IOException {
        /**
         * 细节1:文件中的数据不要换行
         * 细节2:文件不能有BOM头
         */
        File file = new File("chapter18\\f.txt");
//        test1(file);
//        test2(file);
//        test3(file);
        test4(file);
    }

    public static void test1(File src) throws IOException {
        // 1.读取数据
        FileReader fr = new FileReader(src);
        StringBuilder sb = new StringBuilder();

        int ch;
        while ((ch = fr.read()) != -1) {
            sb.append((char) ch);
        }
        fr.close();
        System.out.println(sb);

        // 2.分割数据
        String[] split = sb.toString().split("-");
        // 3.排序数据
        Arrays.sort(split);
        System.out.println(Arrays.toString(split));

        // 4.拼接
        sb = new StringBuilder();
        for (int i = 0; i < split.length; i++) {
            if (i < split.length - 1) {
                sb.append(split[i]).append("-");
            } else {
                sb.append(split[i]);
            }
        }
        System.out.println(sb);

        // 5.续写写出
        FileWriter fw = new FileWriter(src, true);
        fw.write("\n" + sb);

        fw.close();
    }

    // 方式二:使stream流
    public static void test2(File src) throws IOException {
        FileReader fr = new FileReader(src);
        StringBuilder sb = new StringBuilder();

        int len;
        while ((len = fr.read()) != -1) {
            sb.append((char) len);
        }
        System.out.println(sb);

        String[] array = Arrays
                .stream(sb.toString().split("-"))
                .sorted()
                .toArray(String[]::new);
        System.out.println(Arrays.toString(array));

        FileWriter fw = new FileWriter(src, true);
        fw.write("\n");
        for (int i = 0; i < array.length; i++) {
            if (i < array.length - 1) {
                fw.write(array[i] + "-");
            } else {
                fw.write(array[i]);
            }
        }
        fw.close();
    }

    // 方式三:使用集合
    public static void test3(File src) throws IOException {
        FileReader fr = new FileReader(src);
        StringBuilder sb = new StringBuilder();

        int len;
        while ((len = fr.read()) != -1) {
            sb.append((char) len);
        }
        System.out.println(sb);

        List<String> list = Arrays
                .stream(sb.toString().split("-"))
                .sorted()
                .collect(Collectors.toList());

        System.out.println(list);

        FileWriter fw = new FileWriter(src, true);
        fw.write("\n");
        for (int i = 0; i < list.size(); i++) {
            if (i < list.size() - 1) {
                fw.write(list.get(i) + "-");
            } else {
                fw.write(list.get(i) + "");
            }
        }
        fw.close();
    }

    // 使用数组截取
    public static void test4(File src) throws IOException {
        FileReader fr = new FileReader(src);
        StringBuilder sb = new StringBuilder();
        int len;
        while ((len = fr.read()) != -1) {
            sb.append((char) len);
        }
        System.out.println(sb);

        Integer[] array = Arrays
                .stream(sb.toString().split("-"))
                .map(Integer::parseInt)
                .sorted()
                .toArray(Integer[]::new);
        System.out.println(Arrays.toString(array));

        FileWriter fw = new FileWriter(src, true);
        fw.write("\n");
        String replace = Arrays.toString(array).replace(", ", "-");
        System.out.println(replace);
        String result = replace.substring(1, replace.length() - 1);
        System.out.println(result);
        fw.write(result);

        fw.close();
    }
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值