Java_IO流

IO流介绍:

        存储和读取数据的解决方案

        I:input        O:output

        流:像水流一样传输数据

IO流作用:

        用于读写文件中的数据(可以读写文件,或网络中的数据…)

IO流按流向分类:

        1.输出流        程序→文件

        2.输入流        文件→程序

IO流按操作文件的类型分类:

        字节流:可以操作所有类型的文件

        字符流:只能操作纯文本文件

什么是纯文本文件:

        用Windows系统自带的记事本打开并且能读懂的文件

        如:txt文件,md文件,xml文件,lrc文件等

IO流体系结构:

下方蓝色框内都为抽象类,不能直接使用,所以对字节流、字符流体系继续深入

字节流基本流:

  字节输出流

        将程序中的数据读取到本地文件中

书写步骤及细节:

        ①创建字节输出流对象

                细节1:参数是字符串表示的路径或者File对象都是可以的

                细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的

                细节3:如果文件已经存在,则会清空文件

        ②写出数据

                细节:write方法的参数是整数,但是实际写道本地文件中的是整数在ASCII上对应                                     的字符

        ③释放资源

                细节:每次使用完流之后都要释放资源

        先在当前模块中创建一个文件a.txt,在运行下面代码

FileOutputStream简单代码演示:

(暂时不写中文)

public class IODemo1 {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");
        //2.写出数据
        fos.write(97);
        //3.释放资源
        fos.close();
    }
}
运行结果

写出数据的三种方式:
代码演示:
public class IODemo2 {
    public static void main(String[] args) throws IOException {
        //void write(int b)                         一次写一个字节数据
        //void write(byte[] b)                      一次写一个字节数组数据
        //void write(byte[] b, int off, int len)    一次写一个字节数组的部分数据

        //创建对象
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");

        //写出数据
        //1.void write(int b)                         一次写一个字节数据
        fos.write(97);
        fos.write(97);
        //结果为aa
        //2.void write(byte[] b)                      一次写一个字节数组数据
        byte[] arr1 = {98,98,98};
        fos.write(arr1);
        //结果为aabbb
        //3.void write(byte[] b, int off, int len)    一次写一个字节数组的部分数据
        //off 为起始索引
        //len 为个数
        //即写出从起始索引off开始往后len个数组中的元素
        byte[] arr2 = {99,100,101};
        fos.write(arr2,1,2);
        //结果为aabbbde

        //释放资源
        fos.close();
    }
}
运行结果:

换行和续写:
        代码演示:
public class IODemo3 {
    public static void main(String[] args) throws IOException {
        //创建对象
        //续写
        //如果想要续写,打开续写开关即可
        //开关位置:创建对象的第二个参数
        //默认false,表示关闭续写,此时创建对象会清空文件
        //手动传递true,表示打开续写,此时创建对象不会清空文件
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt",true);

        //写入数据
        String str1 = "abcdefg";
        byte[] bytes1 = str1.getBytes();
        fos.write(bytes1);

        //换行
        //换行符 "\r\n"
        String str2 = "\r\n";
        byte[] bytes2 = str2.getBytes();
        fos.write(bytes2);

        String str3 = "123";
        byte[] bytes3 = str3.getBytes();
        fos.write(bytes3);

        //释放资源
        fos.close();
    }
}
        运行结果:

        

        再次运行代码:

        

字节输入流:

        将本地文件中的数据读取到程序中

书写步骤及细节:

        ①创建字节输入对象

                细节:如果文件不存在,就直接报错

        ②读数据

                细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字

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

        ③释放资源

                细节:每次使用完流必须要释放资源

FileInputStream简单代码演示:

(暂时不写中文)

这里让a.txt文件只存储字母abcde

public class IODemo4 {
    public static void main(String[] args) throws IOException {
        //创建对象
        FileInputStream fis = new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");
        
        //读数据
        int n1 = fis.read();
        System.out.println((char)n1);
        int n2 = fis.read();
        System.out.println((char)n2);
        int n3 = fis.read();
        System.out.println((char)n3);
        int n4 = fis.read();
        System.out.println((char)n4);
        int n5 = fis.read();
        System.out.println((char)n5);
        //后面没有元素了再读会返回-1
        int n6 = fis.read();
        System.out.println(n6);

    }
}
运行结果:

循环读取:
代码演示:
public class IODemo5 {
    public static void main(String[] args) throws IOException {
        //创建对象
        FileInputStream fis = new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");

        //读数据
        int b;
        while((b = fis.read()) != -1) {
            System.out.print((char) b);
        }

        //释放资源
        fis.close();
    }
}
运行结果:

文件拷贝

小文件的拷贝:

        将D盘下JavaFileTest文件夹中的a.txt拷贝到当前模块下,存储123,如下

一次一个字节的拷贝:
代码演示:
public class IODemo6 {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        FileInputStream fis = new FileInputStream("D:\\JavaFileTest\\a.txt");
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy.txt");

        //2.拷贝
        //核心思想:边读边写
        int b;
        while((b = fis.read()) != -1) {
            fos.write(b);
        }

        //3.释放资源
        //注意:先开的最后关闭
        fos.close();
        fis.close();
    }
}
运行结果:

一次多个字节的拷贝:
代码演示:
public class IODemo7 {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        FileInputStream fis = new FileInputStream("D:\\JavaFileTest\\a.txt");
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy.txt");

        //2.拷贝
        //核心思想:边读边写
        byte[] bytes = new byte[2];
        //读两次
        int len1 = fis.read(bytes);
        System.out.println(new String(bytes,0,len1));
        int len2 = fis.read(bytes);
        System.out.println(new String(bytes,0,len2));

        //3.释放资源
        //注意:先开的最后关闭
        fos.close();
        fis.close();
    }
}
运行结果:

大文件的拷贝:
代码演示:
public class IODemo8 {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        FileInputStream fis = new FileInputStream("D:\\JavaFileTest\\a.txt");
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy.txt");

        //2.拷贝
        int len;
        byte[] bytes = new byte[1024 * 1024 * 5];//一次拷贝5MB
        while((len = fis.read(bytes)) != -1) {
            System.out.println(len);
            fos.write(bytes,0,len);
        }

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

字符流:

        学习字符流之前我们先学习一下他的前置知识

字符集:

        我们会接触到的字符集有:ASCII、GBK字符集(我国的国家标准字符集)、Unicode字符集(国际标准字符集)

在计算机中的存储规则
ASCII:

GBK字符集:
存储英文:

存储汉字:

编码解码:

总结:

Unicode字符集:

总结:

如何不产生乱码:

编码解码方法:
        代码演示:
public class IODemo9 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "i鲲";
        //编码
        //默认方式编码(我这里使用的是UTF-8)
        byte[] bytes1 = str.getBytes();
        System.out.println(Arrays.toString(bytes1));
        //指定方式编码
        byte[] bytes2 = str.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));

        //解码
        //默认方式解码
        String str3 = new String(bytes1);
        System.out.println(str3);
        //指定方式解码
        String str4 = new String(bytes2,"GBK");
        System.out.println(str4);
    }
}
运行结果:

接下来我们正式学习字符流

字符流基本流

        字符流的底层其实就是字节流

        字符流 = 字节流 + 字符集

特点:

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

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

使用场景

        对于纯文本文件进行读写操作

字符输入流

        先在当前模块创建一个a.txt文件并存入以下信息

FileReader代码演示:
无参read方法
public class IODemo10 {
    public static void main(String[] args) throws IOException {
        //创建对象
        FileReader fr = new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");

        //读取数据
        int ch;
        while((ch = fr.read()) != -1) {
            //细节:读取之后方法底层会进行解码并转成十进制
            //最后返回这个十进制int类型数字
            System.out.print((char)ch);
        }

        //释放资源
        fr.close();
    }
}

运行结果:

有参read方法
public class IODemo11 {
    public static void main(String[] args) throws IOException {
        //创建对象
        FileReader fr = new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");

        //读取数据
        int len;
        char[] chars = new char[2];
        while ((len = fr.read(chars)) != -1) {
            System.out.print(new String(chars,0,len));
        }

        //释放资源
        fr.close();
    }
}

运行结果:

FileWriter代码演示:
构造方法:

成员方法:

书写细节:

代码演示:
public class IODemo12 {
    public static void main(String[] args) throws IOException {
        //创建对象
        FileWriter fw = new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");

        //写入数据
        fw.write(25105);//结果:我
        fw.write("们都");//结果:我们都
        String str = "都要加油";
        fw.write(str,1,3);//结果:我们都要加油
        char[] chars1 = {'a','a','a'};
        fw.write(chars1);//结果:我们都要加油aaa
        char[] chars2 = {'1','!','2'};
        fw.write(chars2,1,1);//结果:我们都要加油aaa!

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

    }
}
运行结果:

字符流原理解析:

使用场景:

        字节流:

                拷贝任意类型的文件

        字符流:

                读取纯文本文件中的数据

                往纯文本文件中写出数据

练习题:

练习一:

        需求:拷贝aaa文件夹,考虑子文件夹

代码演示:
public class IOTest1 {
    public static void main(String[] args) throws IOException {
        //需求:拷贝aaa文件夹,考虑子文件夹

        //创建拷贝文件对象
        File f1 = new File("D:\\JavaFileTest\\aaa");

        //创建目标文件对象
        File f2 = new File("D:\\JavaFileTest\\copy");

        //拷贝
        copyFile(f1,f2);


    }

    //定义方法拷贝一个文件夹
    public static void copyFile(File file,File copy) throws IOException {
        copy.mkdirs();
        File[] files = file.listFiles();
        for (File f : files) {
            if(f.isFile()) {
                //如果是文件
                //直接拷贝
                FileInputStream fis = new FileInputStream(f);
                FileOutputStream fos = new FileOutputStream(new File(copy,f.getName()));
                byte[] bytes = new byte[1024 * 1024 * 5];
                int len;
                while((len = fis.read(bytes)) != -1) {
                    fos.write(bytes,0,len);
                }
                fos.close();
                fis.close();

            } else {
                //如果是文件夹
                //重新调用
                copyFile(f,new File(copy,f.getName()));
            }
        }
    }
}
运行结果:
aaa文件夹:

copy文件夹:

练习二:

        文件加密

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

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

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

代码演示:
public class IOTest2 {
    public static void main(String[] args) throws IOException {
        //文件加密
        /*
        为了保证文件的安全性,就需要对原始文件进行加密存储,
        再使用的时候再对其进行解密处理。
        加密原理:
            对原始文件中的每一个字节数据进行更改
            然后将更改以后的数据存储到新的文件中
        解密原理:
            读取加密之后的文件,按照加密的规则反向操作,变成原始文件
         */

        //将a.txt文件中的内容加密存储到b.txt中

        //创建文件对象
        File f1 = new File("D:\\JavaFileTest\\a.txt");
        File f2 = new File("D:\\JavaFileTest\\b.txt");

        //创建输入输出流对象
        FileInputStream fis1 = new FileInputStream(f1);
        FileOutputStream fos1 = new FileOutputStream(f2);

        //加密拷贝
        int src;
        while((src = fis1.read()) != -1) {
            src = src + 99;
            fos1.write(src);
        }

        //释放资源
        fos1.close();
        fis1.close();


        //将b.txt文件中的内容解密拷贝到c.txt中

        //创建文件对象
        File f3 = new File("D:\\JavaFileTest\\c.txt");

        //创建输入输出流对象
        FileInputStream fis2 = new FileInputStream(f2);
        FileOutputStream fos2 = new FileOutputStream(f3);

        //解密拷贝
        int encryptSrc;
        while((encryptSrc = fis2.read()) != -1) {
            encryptSrc = encryptSrc - 99;
            fos2.write(encryptSrc);
        }

        //释放资源
        fos2.close();
        fis2.close();

    }
}

(或者通过字节数异或(^)一个数字,解密时再异或这个数字,也可以)

运行结果:
a.txt:

b.txt

c.txt

练习三:

        文本文件中有以下的数据:

                 2-1-9-4-7-8

        将文件中的数据进行排序

        变成以下的数据:

                 1-2-4-7-8-9

代码演示1:
public class IOTest3 {
    public static void main(String[] args) throws IOException {
            /*
            文本文件中有以下的数据:
                 2-1-9-4-7-8
            将文件中的数据进行排序,变成以下的数据:
                  1-2-4-7-8-9
             */

        //读取数据
        FileReader fr = new FileReader("D:\\JavaFileTest\\num.txt");
        //创建StringBuilder对象
        StringBuilder sb = new StringBuilder();
        int ch;
        while((ch = fr.read()) != -1) {
            sb.append((char) ch);
        }
        //释放资源
        fr.close();


        //排序
        ArrayList<Integer> list = new ArrayList<>();
        String str = sb.toString();
        String[] arrStr = str.split("-");
        for (String s : arrStr) {
            int i = Integer.parseInt(s);
            list.add(i);
        }
        Collections.sort(list);

        //写出数据
        FileWriter fw = new FileWriter("D:\\JavaFileTest\\num.txt");
        System.out.println(list.size());
        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();
    }
}
运行结果:

代码演示2:

public class IOTest4 {
    public static void main(String[] args) throws IOException {
        /*
            文本文件中有以下的数据:
                 2-1-9-4-7-8
            将文件中的数据进行排序,变成以下的数据:
                  1-2-4-7-8-9
             */

        FileReader fr = new FileReader("D:\\JavaFileTest\\num.txt");
        StringBuilder sb = new StringBuilder();
        int ch;
        while((ch = fr.read()) != -1) {
            sb.append((char) ch);
        }
        //释放资源
        fr.close();
        Integer[] arr = Arrays.stream(sb.toString()
                .split("-"))
                .map(Integer::parseInt)
                .sorted().toArray(Integer[]::new);
        //arr:[1, 2, 4, 7, 8, 9]

        //写出
        FileWriter fw = new FileWriter("D:\\JavaFileTest\\num.txt");
        String str = new String(Arrays.toString(arr));
        String resultStr = str.replace(",", "-").substring(1, str.length() - 1);
        fw.write(resultStr);
        fw.close();
    }
}
运行结果:

高级流

        高级流都可以装基本流

1.缓冲流

体系结构:

字节缓冲流:

        原理:

                底层自带了长度为8192的缓冲区提高性能

        构造方法:

        代码演示:
        一次读写一个字节
public class BufferDemo1 {
    public static void main(String[] args) throws IOException {
        //创建缓冲流对象
        //参数必须为基本流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy.txt"));

        //拷贝
        int b;
        while((b = bis.read()) != -1) {
            bos.write(b);
        }

        //释放资源
        //高级流释放资源后基本流也被释放
        bos.close();
        bis.close();
    }
}
        运行结果:

                copy.txt:

                        

                        成功拷贝

        一次读写一个字节数组:
public class BufferDemo2 {
    public static void main(String[] args) throws IOException {
        //创建缓冲流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy2.txt"));

        //拷贝
        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes)) != -1) {
            bos.write(bytes,0,len);
        }

        //释放资源
        bos.close();
        bis.close();
    }
}
        运行结果:

                copy2.txt:

                        

提高效率原理:

        1.底层是靠基本流,字节输入流将数据存入到内存的长度为8192的缓冲区里。

        2.无参情况下,定义int类型b接收,则int b会一个一个的将输入流缓冲区的数据运送到输出流缓冲区。

           参数为字节数组情况下,则是字节数组多个多个的将输入流缓冲区的数据运送到输出流缓冲区。

        3.内存中缓冲区之间的数据传输很快,真正节约时间的地方是基本流通过缓冲区读和写数据。

字符缓冲流:

        原理:

                底层自带大小为8192的字符缓冲区提高性能,长度为8192 * 2 = 16384。

        特有方法:

        代码演示:
public class BufferDemo3 {
    public static void main(String[] args) throws IOException {
        //创建字符输入缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));

        //读一行数据
        String str = br.readLine();
        System.out.println(str);

        //释放资源
        br.close();


        //创建字符输出缓冲流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\b.txt"));

        //写数据
        bw.write("123456");
        //换行(可跨平台)
        bw.newLine();
        bw.write("789");

        //释放资源
        bw.close();
    }
}
运行结果:

b.txt:

练习题:

练习一:

        拷贝文件

        四种方式拷贝文件,并统计各自用时

        1.字节流的基本流:一次读写一个字节

        2.字节流的基本流:一次读写一个字节数组

        3.字节缓冲流:一次读写一个字节

        4.字节缓冲流:一次读写一个字节数组

代码演示:
public class BufferTest1 {
    public static void main(String[] args) throws IOException {
    //拷贝文件
    /*
    四种方式拷贝文件,并统计各自用时
    1.字节流的基本流:一次读写一个字节
    2.字节流的基本流:一次读写一个字节数组
    3.字节缓冲流:一次读写一个字节
    4.字节缓冲流:一次读写一个字节数组
    */

        //创建被拷贝文件对象
        File f1 = new File("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt");

        //1.字节流的基本流:一次读写一个字节
        //1)创建对象
        FileInputStream fis1 = new FileInputStream(f1);
        FileOutputStream fos1 = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy1.txt");
        //2)拷贝
        //定义起始时间
        long start1 = System.currentTimeMillis();
        int ch;
        while((ch = fis1.read()) != -1) {
            fos1.write(ch);
        }
        //定义结束时间
        long end1 = System.currentTimeMillis();
        //输出总耗时
        System.out.println("1.字节流的基本流:一次读写一个字节  总耗时:" + (end1 - start1) + "毫秒");
        //3)释放资源
        fos1.close();
        fis1.close();


        //2.字节流的基本流:一次读写一个字节数组
        //1)创建对象
        FileInputStream fis2 = new FileInputStream(f1);
        FileOutputStream fos2 = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy2.txt");
        //2)拷贝
        //创建字符数组
        byte[] bytes = new byte[1024];
        int len;
        //定义起始时间
        long start2 = System.currentTimeMillis();
        while((len = fis2.read(bytes)) != -1) {
            fos2.write(bytes,0,len);
        }
        //定义结束时间
        long end2 = System.currentTimeMillis();
        //输出总耗时
        System.out.println("2.字节流的基本流:一次读写一个字节  总耗时:" + (end2 - start2) + "毫秒");
        //3)释放资源
        fos2.close();
        fis2.close();


        //3.字节缓冲流:一次读写一个字节
        //1)创建对象
        BufferedInputStream bis1 = new BufferedInputStream(new FileInputStream(f1));
        BufferedOutputStream bos1 = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy3.txt"));
        //2)拷贝
        //定义起始时间
        long start3 = System.currentTimeMillis();
        int b;
        while((b = bis1.read()) != -1) {
            bos1.write(b);
        }
        //定义结束时间
        long end3 = System.currentTimeMillis();
        //输出总耗时
        System.out.println("3.字节缓冲流:一次读写一个字节  总耗时:" + (end3 - start3) + "毫秒");
        //3)释放资源
        bos1.close();
        bis1.close();


        //4.字节缓冲流:一次读写一个字节数组
        //1)创建对象
        BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(f1));
        BufferedOutputStream bos2 = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\copy4.txt"));
        //2)拷贝
        //定义起始时间
        long start4 = System.currentTimeMillis();
        byte[] bytes2 = new byte[1024];
        int len2;
        while((len2 = bis2.read(bytes2)) != -1) {
            bos2.write(bytes2,0,len2);
        }
        //定义结束时间
        long end4 = System.currentTimeMillis();
        //输出总耗时
        System.out.println("4.字节缓冲流:一次读写一个字节数组  总耗时:" + (end4 - start4) + "毫秒");
        //3)释放资源
        bos2.close();
        bis2.close();
    }
}
运行结果:

全部正常拷贝

练习二:

        需求:把《出师表》的文章顺序进行恢复,并存入到一个新文件中

已给出的乱序文件:

csb.txt:

(注意:每一序号只占一行)

3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。

代码演示:
public class BufferTest2 {
    public static void main(String[] args) throws IOException {
        //需求:把文件csb.txt中的《出师表》的文章顺序进行恢复,并存入到一个新文件中

        //
        //创建乱序《出师表》文件对象
        File f1 = new File("C:\\Users\\Han\\IdeaProjects\\myio\\csb.txt");

        //创建字符缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader(f1));
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\csb2.txt"));

        //读取数据
        //创建集合存储每个序号及字符串
        //默认从小到大排序
        TreeMap<Integer,String> tm = new TreeMap<>();
        String str;
        while((str = br.readLine()) != null) {
            String[] arr = str.split("\\.");
            int num = Integer.parseInt(arr[0]);
            str = arr[1];
            tm.put(num,str);
        }

        //写入数据
        tm.forEach((Integer num, String string) -> {
                try {
                    bw.write(string);
                    bw.newLine();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
        });

        //释放资源
        bw.close();
        br.close();

    }
}
运行结果:

成功拷贝

练习三:

实现一个验证程序运行次数的小程序,要求如下

        1.当程序运行超过3次时给出提示:本软件只能免费使用3次,欢迎您注册会员后继续使用

        2.程序运行演示如下:

                第一次运行控制台输出:欢迎使用本软件,第1次使用免费

                第二次运行控制台输出:欢迎使用本软件,第2次使用免费

                第三次运行控制台输出:欢迎使用本软件,第3次使用免费

                第四次及之后进行控制台输出:本软件只能免费使用3次,欢迎您注册会员后继续使用

代码演示:

先创建一个文件存储一个0。

public class BufferTest3 {
    public static void main(String[] args) throws IOException {
        /*
        实现一个验证程序运行次数的小程序,要求如下
        1.当程序运行超过3次时给出提示:本软件只能免费使用3次,欢迎您注册会员后继续使用
        2.程序运行演示如下:
                第一次运行控制台输出:欢迎使用本软件,第1次使用免费
                第二次运行控制台输出:欢迎使用本软件,第2次使用免费
                第三次运行控制台输出:欢迎使用本软件,第3次使用免费
                第四次及之后进行控制台输出:本软件只能免费使用3次,欢迎您注册会员后继续使用
         */

        //创建字符缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt"));

        //读取数据
        String countStr = br.readLine();
        br.close();
        //转换类型
        int count = Integer.parseInt(countStr);
        count++;
        //判断
        if(count > 3) {
            System.out.println("本软件只能免费使用3次,欢迎您注册会员后继续使用");
            //退出程序
            System.exit(0);
        } else {
            //num = 0,1,2
            System.out.println("欢迎使用本软件,第" + count + "次使用免费");
        }
        //重新写入
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt"));
        bw.write(count + "");

        bw.close();

    }
}
运行结果:
测试了五次

2.转换流:

        属于字符流

        字符转换输入流:InputStreamReader        字符转换输出流OutputStreamWriter

作用:

        指定字符集读写数据(JDK11之后被淘汰)

        字节流想要使用字符流中的方法时可以使用转换流

按照指定字符编码读取

先创建一个文本文件gbkfile.txt存储如下信息

再改变编码方式为GBK并存入当前模块下,由于idea默认使用UTF-8,文件会显示乱码,如下

代码演示:

        FileReader是转换流的子类,这种方法目前是主流方法

public class ConvertDemo1 {
    public static void main(String[] args) throws IOException {
        //按照指定字符编码读取

        //创建字符输入流对象
        FileReader fr = new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\gbkfile.txt", Charset.forName("GBK"));

        //读取
        int ch;
        while((ch = fr.read()) != -1) {
            System.out.print((char) ch);
        }

        //释放资源
        fr.close();
    }
}
运行结果:

按照指定字符编码写入

代码演示:
public class ConvertDemo2 {
    public static void main(String[] args) throws IOException {
        //按照指定字符编码写入

        //创建对象
        FileWriter fw = new FileWriter("D:\\JavaFileTest\\gbkfile.txt", Charset.forName("GBK"));

        //写入
        fw.write("中国中国");

        //释放资源
        fw.close();
    }
}
运行结果:

        GBK编码文件正常显示

练习一:

        将一个GBK编码文件转成一个UTF-8文件

代码演示:
public class ConvertDemo3 {
    public static void main(String[] args) throws IOException {
        //将一个GBK编码文件转成一个UTF-8文件

        //创建对象
        FileReader fr = new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\gbkfile.txt", Charset.forName("GBK"));
        FileWriter fw = new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\uft-8file.txt",Charset.forName("UTF-8"));

        //转换
        int ch;
        while((ch = fr.read()) != -1) {
            fw.write((char) ch);
        }

        //释放资源
        fw.close();
        fr.close();
    }
}
运行结果:

练习二:

        利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码

        注意:1.字节流读取中文时会出现乱码(字符流不会)

                   2.字节流中没有读一整行的方法(字符缓冲流有)

代码演示:
public class ConvertDemo4 {
    public static void main(String[] args) throws IOException {
            /*
            利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
                注意: 1.字节流读取中文时会出现乱码(字符流不会)
                      2.字节流中没有读一整行的方法(字符缓冲流有)
            */

       /*
       //先创建字节流对象
        FileInputStream fis = new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\uft-8file.txt");
        //转换成字符对象
        InputStreamReader isr = new InputStreamReader(fis);
        //转换成字符缓冲流对象
        BufferedReader br = new BufferedReader(isr);
        */

        BufferedReader br = new BufferedReader(new InputStreamReader
                (new FileInputStream("C:\\\\Users\\\\Han\\\\IdeaProjects\\\\myio\\\\uft-8file.txt")));

        //读取数据
        String str;
        while ((str = br.readLine()) != null) {
            System.out.println(str);
        }

        //释放资源
        br.close();
    }
}
运行结果:

3.序列化流和反序列化流:

        序列化流和反序列化流属于字节流

序列化流/对象操作输出流

        可以把Java中的对象写到本地文件中

代码演示:
Student类:

注意:序列化流/对象操作输出流传递的对象必须实现接口Serializable,运行后才不会报错

public class Student implements Serializable {
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
测试类:
public class ObjectStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        需求:利用序列化流/对象操作输出流,把一个对象写道本地文件中。
        构造方法:
                public ObjectOutputStream(OutputStream out)     把基本流变成高级流
        成员方法:
                public final void writeObject(Object obj)       把对象序列化(写出)到文件中去
         */

        //创建学生对象
        Student stu = new Student("han",21);

        //创建序列化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));

        //写入
        oos.writeObject(stu);

        //释放资源
        oos.close();
    }
}
运行结果:

反序列化流/对象操作输入流

        可以把序列化到本地文件中的对象,读取到程序中来

代码演示:
public class ObjectStreamDemo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*
        需求:利用反序列化流/对象操作输入流,把文件中的对象读到程序当中。
        构造方法:
                public ObjectInputStream(InputStream out)     把基本流变成高级流
        成员方法:
                public Object readObject()       把序列化(写出)到本地文件中的对象,读取到程序中来
         */

        //创建反序列化流/对象操作输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));

        //读取对象
        Object o = ois.readObject();

        //打印对象
        System.out.println(o);

        //释放资源
        ois.close();
    }
}
运行结果:

细节:

        ①使用序列化流将对象写道文件时,需要让Javabean类实现Serializable接口。否则,会出现NotSerializableException异常

        ②序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了

        ③序列化对象后,修改了Javabean类,再次反序列化,会抛出InvalidClassException异常

                解决方案:给JavaBean类添加serialVersionUID(序列号、版本号)

                如:

public class Student implements Serializable {

    private static final long serialVersionUID = -5805697296856704486L;
    private String name;
    private int age;
}

        ④如果一个对象中的某个成员变量的值不想被序列化,该如何实现

                解决方案:给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列                                      化过程,输出这个对象时这个变量会显示null

                如:

public class Student implements Serializable {

    private static final long serialVersionUID = -5805697296856704486L;
    private String name;
    private int age;
    private transient String address;
}

练习题:

        需求:将多个自定义对象序列化到文件中,但是对象的个数不确定,该如何操作?

答:可以在写入对象时,将所有对象存入集合中,直接写入集合对象,这样读取时再将读到的对象转成集合,就可以得到所有对象。

代码演示:
写入多个对象:
public class ObjectStreamTest1 {
    public static void main(String[] args) throws IOException {
        /*
        需求:将多个自定义对象序列化到文件中,但是对象的个数不确定,该如何操作?
         */

        //创建学生对象
        Student s1 = new Student("han",21);
        Student s2 = new Student("ma",22);
        Student s3 = new Student("zhao",21);
        //创建集合并存储所有学生对象
        ArrayList<Student> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        //创建序列流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));
        //写入
        oos.writeObject(list);
        //释放资源
        oos.close();

    }
}
读取:
public class ObjectStreamTest2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //创建反序列流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));
        //读取
        ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
        //释放资源
        ois.close();
        //输出
        for (Student student : list) {
            System.out.println(student);
        }

    }
}
运行结果:

4.打印流

(我们经常使用的System.out.println语句就使用了打印流,不信你可以看看System.out的返回值)

分类:

        分为字节打印流和字符打印流两种

特点:

        1.打印流只操作文件目的地,不操作数据源

        2.特有的写出方法可以实现数据原样写出

        3.特有的写出方法可以实现自动刷新、自动换行

字节打印流:

        在OutputStream下

构造方法:

成员方法:

代码演示:
public class PrintStreamDemo1 {
    public static void main(String[] args) throws FileNotFoundException {
        //创建字节打印流对象
        PrintStream ps = new PrintStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));

        //写入
        ps.println(97);
        ps.print(true);
        ps.println();
        ps.printf("%s繁荣昌盛","中国");

        //释放资源
        ps.close();
    }
}
运行结果:

字符打印流

        在Writer下

特点:

        字符流底层有缓冲区,且如果想要自动刷新则需要手动开启

构造方法和成员方法:

        字符打印流的构造方法和成员方法与字节打印流一致,不同的地方是创建字符打印流时,默认是关闭自动刷新,第二个参数写“true”才是开启自动刷新,而且包装的基本类为FileWriter。

代码演示:
public class PrintStreamDemo2 {
    public static void main(String[] args) throws IOException {
        //创建字符打印流对象
        PrintWriter pw = new PrintWriter(
                new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\a.txt"));

        //写入
        pw.println(97);
        pw.print(true);
        pw.println();
        pw.printf("%s繁荣昌盛","中国");

        //释放资源
        pw.close();
    }
}
运行结果:

5.解压缩流和压缩流

解压缩流:

        位于InputStream下

解压本质:

        压缩包中的每一个文件都是一个ZipEntry,解压本质就是把每一个按照层级拷贝到本地另一个文件夹中。

        解压一个aaa文件,aaa文件中包含一个b.txt,一个ccc文件夹,ccc文件夹中有一个c.txt,txt文件中都存储txt文件本身的名字,如b.txt中存储b.txt

代码演示:
public class ZipStreamDemo1 {
    public static void main(String[] args) throws IOException {
        //解压一个文件

        //创建要解压缩文件对象
        File src = new File("D:\\aaa.zip");
        //创建目标文件对象
        File dest = new File("D:\\");

        //解压缩
        unzip(src, dest);

    }

    public static void unzip(File src, File dest) throws IOException {
        //创建解压缩流对象
        ZipInputStream zis = new ZipInputStream(new FileInputStream(src));

        //遍历得到每个文件
        ZipEntry entry;
        while((entry = zis.getNextEntry()) != null) {
            System.out.println(entry);
            //判断
            if(entry.isDirectory()) {
                //文件夹:需到目标文件夹中创建一个同样的文件夹
                File file = new File(dest,entry.getName());
                boolean mkdirs = file.mkdirs();
                System.out.println(mkdirs);
            } else {
                //文件:需要在目标文件中按照同级目录存放
                //存放需要创建字节输出对象
                FileOutputStream fos = new FileOutputStream(new File(dest, entry.getName()));
                //写入
                int b;
                while((b = zis.read()) != -1) {
                    fos.write(b);
                }
                fos.close();

                zis.closeEntry();
            }
        }
        zis.close();
    }
}
运行结果:

        成功解压

压缩流:

        位于OutputSream下

压缩本质:

        把每一个文件/文件夹看出ZipEntry对象放到压缩包中

压缩一个文件:
代码演示:
public class ZipStreamDemo2 {
    public static void main(String[] args) throws IOException {
        //压缩单个文件

        //创建被压缩文件
        File src = new File("D:\\a.txt");
        //创建目标文件
        File dest = new File("D:\\");

        tozip(src,dest);

    }

    //定义方法压缩文件
    public static void tozip(File src, File dest) throws IOException {
        //创建压缩流
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(
                new File(dest, src.getName().split("\\.")[0] + ".zip")));
        //创建ZipEntry对象
        ZipEntry entry = new ZipEntry(src.getName());
        //把ZipEntry对象存入压缩包
        zos.putNextEntry(entry);
        //存入数据
        FileInputStream fis = new FileInputStream(src);
        int b;
        while((b = fis.read()) != -1) {
            zos.write(b);
        }
        //释放资源
        fis.close();
        zos.closeEntry();
        zos.close();
    }
}
运行结果:

        成功压缩

压缩一个文件夹

        注意:创建ZipEntry对象时的参数也表示压缩包里面的路径

代码演示:
public class ZipStreamDemo3 {
    public static void main(String[] args) throws IOException {
        //压缩一个文件夹

        //创建要压缩的文件夹的File对象
        File src = new File("D:\\aaa");
        //创建表示压缩包的路径的File对象
        File dest = new File(src.getParent() + src.getName() + ".zip");

        //压缩文件夹
        //创建压缩流对象
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
        //获取src中的每一个文件,变成ZipEntry对象,放入到压缩包中
        toZip(src,zos,src.getName());
        //释放资源
        zos.close();
    }

    public static void toZip(File src, ZipOutputStream zos, String name) throws IOException {
        //进入src文件夹遍历每个文件
        File[] files = src.listFiles();
        for (File file : files) {
            //判断
            if(file.isFile()) {
                //文件:变成ZipEntry对象,再放到压缩包中
                ZipEntry entry = new ZipEntry(name + "\\" + file.getName());
                zos.putNextEntry(entry);
                //读取文件中的数据,写道压缩包中
                FileInputStream fis = new FileInputStream(file);
                int b;
                while((b = fis.read()) != -1) {
                    zos.write(b);
                }
                fis.close();
                zos.closeEntry();
            } else {
                //文件夹:递归
                toZip(file,zos,name + "\\" + file.getName());
            }
        }
    }
}
运行结果:

        成功压缩

常用工具包

1.Commons-io

文件/文件夹相关:

流相关:

简单代码演示:

public class CommonsIODemo {
    public static void main(String[] args) throws IOException {
        //拷贝一个文件
        File src = new File("D:\\a.txt");
        File dest = new File("D:\\copy.txt");
        FileUtils.copyFile(src,dest);

        //拷贝一个文件夹
        File src2 = new File("D:\\aaa");
        File dest2 = new File("D:\\copy");
        FileUtils.copyDirectory(src2,dest2);

        //拷贝一个文件夹放入另一个文件夹中
        File src3 = new File("D:\\aaa");
        File dest3 = new File("D:\\bbb");
        FileUtils.copyDirectoryToDirectory(src3,dest3);

        //删除文件夹
        FileUtils.deleteDirectory(dest3);

        //清空文件夹
        FileUtils.cleanDirectory(dest2);
    }
}

2.hutool

简单代码演示:

public class HutoolIODemo {
    public static void main(String[] args) {
        //创建文件对象    参数最后可以写多个字符串表示层级
        File file = FileUtil.file("D:\\", "aaa", "bbb", "a.txt");
        System.out.println(file);
        //创建一个文件
        //他的特点是父级路径不存在情况下,他会创建父级路径,而不是报错
        File touch = FileUtil.touch(file);
        System.out.println(touch);

        //把集合中的数据写出到文件夹中,覆盖模式
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("aaa");
        list1.add("bbb");
        list1.add("ccc");
        FileUtil.writeLines(list1,file,"UTF-8");

        //把集合中的数据写出到文件夹中,续写模式
        FileUtil.appendLines(list1,file,"UTF-8");

        //把文件中的数据读到集合中
        List<String> list2 = FileUtil.readLines(file, "UTF-8");
        System.out.println(list2);
    }
}
运行结果:

详细信息:

官网:
    https://hutool.cn/
API文档:
    https://apidoc.gitee.com/dromara/hutool/

中文使用文档:
    https://hutool.cn/docs/#/

配置文件

properties:

        properties是一个双列集合,拥有Map集合的所有的特点

        重点:除此之外,它还有一些特有的方法,可以把集合中的数据,按照键值对的形式写道配置文件当中,也可以把配置文件中的数据,读取到集合中来。

代码演示:
store存入
public class PropertiesDemo2 {
    public static void main(String[] args) throws IOException {
        //创建对象
        Properties prop = new Properties();

        //读取数据
        FileInputStream fis = new FileInputStream("C:\\Users\\Han\\IdeaProjects\\myio\\src\\com\\han\\io\\properties\\a.properties");
        prop.load(fis);
        fis.close();

        System.out.println(prop);
    }
}
load读取
public class PropertiesDemo1 {
    public static void main(String[] args) throws IOException {
        //创建对象
        Properties prop = new Properties();
        //存入数据
        prop.put("aaa","111");
        prop.put("bbb","222");
        prop.put("ccc","333");
        prop.put("ddd","444");

        //存入文件
        //创建输出流
        FileOutputStream fos = new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\myio\\src\\com\\han\\io\\properties\\a.properties");
        prop.store(fos,"test");//后面的参数为标注
        fos.close();
    }
}
运行结果:
store存入

load读取

综合练习:

练习一:

生成假数据:

方法一:

在指定网址上爬取百家姓
网址:https://hanyu.baidu.com/shici/detail?from=aladdin&pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d
(浏览器搜百家姓,点击百度汉语那个网址)
在指定网址上分别爬取男生姓名和女生姓名
男生姓名网址:"http://www.haoming8.cn/baobao/10881.html"
    格式:xx、xx、xx、xx、xx、...xx。
女生姓名网址:"http://www.haoming8.cn/baobao/7641.html"
    格式:
        xx xx xx xx xx
        xx xx xx xx xx
        xx xx xx xx xx
        ...
将男生女生的指定个数的姓和名随机拼接后以 姓名(唯一)-性别-随机年龄(18~22) 这种格式写出
代码演示:
public class Test1 {
    public static void main(String[] args) throws IOException {
        /*
        在指定网址上爬取百家姓
        网址:https://hanyu.baidu.com/shici/detail?from=aladdin&pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d
        (浏览器搜百家姓,点击百度汉语那个网址)
        在指定网址上分别爬取男生姓名和女生姓名
        男生姓名网址:"http://www.haoming8.cn/baobao/10881.html"
            格式:xx、xx、xx、xx、xx、...xx。
        女生姓名网址:"http://www.haoming8.cn/baobao/7641.html"
            格式:
                xx xx xx xx xx
                xx xx xx xx xx
                xx xx xx xx xx
                ...
        将男生女生的指定个数的姓和名随机拼接后以姓名(唯一)-性别-随机年龄(18~22)这种格式写出
         */

        //1.定义变量记录网址
        String familyNamaNet = "https://hanyu.baidu.com/shici/detail?from=aladdin&pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        String girlNameNet = "http://www.haoming8.cn/baobao/7641.html";

        //2.爬取数据,把网址上的所有数据拼接成一个字符串
        String familyNameStr = webCrawler(familyNamaNet);
        String boyNameStr = webCrawler(boyNameNet);
        String girlNameStr = webCrawler(girlNameNet);

        //3.使用正则表达式筛选百家姓
        ArrayList<String> familyNameTempList = getDate(familyNameStr,"(\\W{4})(,|。)", 1);
        //System.out.println(familyNameTempList);
        ArrayList<String> boyNameTempList = getDate(boyNameStr, "(、)([\u4E00-\u9FA5]{2})(、|。)", 2);//这样会忽略掉每段第一个姓名,但是不添加前面的(、)又会多添加一些非姓名元素,希望有大佬可以在评论区指导一下小弟
        //System.out.println(boyNameTempList);
        ArrayList<String> girlNameTempList = getDate(girlNameStr, "([\u4E00-\u9FA5]{2} ){4}[\u4E00-\u9FA5]{2}", 0);
        //System.out.println(girlNameTempList);

        //4.处理数据
        //百家姓
        ArrayList<String> familyNameList = new ArrayList<>();
        for (String str : familyNameTempList) {
            for (int i = 0; i < str.length(); i++) {
                familyNameList.add(str.charAt(i) + "");
            }
        }
        //System.out.println(familyNameList);

        //男生姓名可以直接用
        ArrayList<String> boyNameList = new ArrayList<>(boyNameTempList);
        //System.out.println(boyNameList);

        //女生姓名
        ArrayList<String> girlNameList = new ArrayList<>();
        for (String str : girlNameTempList) {
            String[] arr = str.split(" ");
            for (String name : arr) {
                girlNameList.add(name);
            }
        }
        //System.out.println(girlNameList);


        //5.以指定格式输出
        ArrayList<String> list = getInfos(familyNameList, boyNameList, girlNameList, 50, 20);

        //6.写出数据
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\infos.txt"));
        for (String info : list) {
            bw.write(info);
            bw.newLine();
        }
        bw.close();
    }

    //创建方法将指定个数的男生女生随机姓名以规定格式存入集合
    //参数一:百家姓集合 参数二:男生名字集合 参数三:女生名字集合 参数四:生成男生姓名的个数 参数五:生成女生姓名的个数
    public static ArrayList<String> getInfos(ArrayList<String> familyNameList,
                                             ArrayList<String> boyNameList, ArrayList<String> girlNameList,
                                             int boyCount, int girlCount) {
        ArrayList<String> infosList = new ArrayList<>();
        //存储指定个数个男生姓名
        HashSet<String> boysHs = new HashSet();
        while(boysHs.size() != boyCount) {
            Collections.shuffle(familyNameList);
            Collections.shuffle(boyNameList);
            boysHs.add(familyNameList.get(0) + boyNameList.get(0));
        }

        //存储指定个数个女生姓名
        HashSet<String> girlsHs = new HashSet();
        while(girlsHs.size() != girlCount) {
            Collections.shuffle(familyNameList);
            Collections.shuffle(girlNameList);
            girlsHs.add(familyNameList.get(0) + girlNameList.get(0));
        }

        //转换成指定格式 姓名-性别-年龄(随机 18~22)
        //男
        for (String boyName : boysHs) {
            Random r = new Random();
            int age = r.nextInt(5) + 18;
            infosList.add(boyName + "-男" + "-" + age);
        }
        //女
        for (String girlName : girlsHs) {
            Random r = new Random();
            int age = r.nextInt(5) + 18;
            infosList.add(girlName + "-女" + "-" + age);
        }

        return infosList;
    }


    //利用正则表达式筛选百家姓
    public static ArrayList<String> getDate(String str, String regex, int index) {
        //创建集合存储数据
        ArrayList<String> list = new ArrayList<>();
        //创建正则表达式对象
        Pattern pattern = Pattern.compile(regex);
        //按照pattern的规则,到str中获取数据
        Matcher matcher = pattern.matcher(str);
        while(matcher.find()) {
            list.add(matcher.group(index));
        }
        return list;
    }


    //爬取网络数据
    private static String webCrawler(String net) throws IOException {
        //创建StringBuilder对象,便于拼接字符串
        StringBuilder sb = new StringBuilder();
        //创建URL对象
        URL url = new URL(net);
        //连接到网址
        URLConnection conn = url.openConnection();
        //读取数据
        //有中文,需使用字符流
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        int ch;
        while((ch = isr.read()) != -1) {
            sb.append((char) ch);
        }
        isr.close();
        return sb.toString();
    }
}

(筛选男生姓名的正则表达式有瑕疵,代码中已详细描述,希望各位有头绪可以在评论区可以指导一下小弟,谢谢各位了)

运行结果:

(只截取了一部分)

方法二:

        使用hutool包爬取数据生成假数据,按照练习一的要求,但不要求第四步处理数据及后续步骤

代码演示:
public class Test2 {
    public static void main(String[] args) throws IOException {
        //使用hutool包爬取数据生成假数据,按照练习一的要求,但不要求第四步处理数据及后续步骤
        
        //1.定义变量记录网址
        String familyNamaNet = "https://hanyu.baidu.com/shici/detail?from=aladdin&pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        String girlNameNet = "http://www.haoming8.cn/baobao/7641.html";
        
        //2.爬取数据
        String familyNameStr = HttpUtil.get(familyNamaNet);
        String boyNameStr = HttpUtil.get(boyNameNet);
        String girlNameStr = HttpUtil.get(girlNameNet);

        //3.使用正则表达式筛选
        List<String> familyNameList = ReUtil.findAll("(\\W{4})(,|。)", familyNameStr, 1);
        List<String> boyNameList = ReUtil.findAll("(、)([\u4E00-\u9FA5]{2})(、|。)", boyNameStr, 2);
        List<String> girlNameList = ReUtil.findAll("([\u4E00-\u9FA5]{2} ){4}[\u4E00-\u9FA5]{2}", girlNameStr, 0);

        //4.输出结果
        System.out.println(familyNameList);
        System.out.println(boyNameList);
        System.out.println(girlNameList);
    }
}
运行结果:

(只截取了一部分)

练习二

需求一:

需求:
    有一个文件里面存储了一个班级学生的信息,
    每个学生的信息占一行,
    格式为:姓名-性别-年龄
    要求通过程序实现随机点名器
运行效果:
    第一次运行程序:随机姓名1(只显示名字)
    第二次运行程序:随机姓名2(只显示名字)
    第三次运行程序:随机姓名3(只显示名字)
    ...
代码演示:
public class Test2_1 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
            有一个文件里面存储了一个班级学生的信息,
            每个学生的信息占一行,
            格式为:姓名-性别-年龄
            要求通过程序实现随机点名器
        运行效果:
            第一次运行程序:随机姓名1(只显示名字)
            第二次运行程序:随机姓名2(只显示名字)
            第三次运行程序:随机姓名3(只显示名字)
            ...
         */

        //创建存储信息文件对象
        File file = new File("C:\\Users\\Han\\IdeaProjects\\myio\\infos.txt");

        //将学生信息拷贝到集合中
        //创建字符缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader(file));
        //创建集合存储所有学生信息
        ArrayList<String> infosList = new ArrayList<>();
        //拷贝
        String str;
        while((str = br.readLine()) != null) {
            infosList.add(str);
        }
        br.close();

        //随机点名
        //打乱集合 获取随机姓名
        Collections.shuffle(infosList);
        String name = infosList.get(0).split("-")[0];
        //添加第几次运行程序信息
        //读取文档内数字(初始存放0)
        FileReader fr = new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt");
        int chInt = fr.read();
        chInt++;
        char ch = (char) chInt;
        fr.close();
        System.out.println("第" + ch + "次运行程序:" + name);
        FileWriter fw = new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt");
        fw.write(chInt);
        fw.close();

    }
}
运行结果:

第一次:

第二次:

第三次:

需求二:

随机点名器2
需求:
    有一个文件里面存储了一个班级学生的信息,
    每个学生的信息占一行,
    格式为:姓名-性别-年龄
    要求通过程序实现随机点名器
运行效果:
    70%概率随机到男生
    30%概率随机到女生
    总共随机100万次,统计结果
    观察结果是否满足
代码演示:
public class Test2_2 {
    public static void main(String[] args) throws IOException {
        /*
        随机点名器2
        需求:
            有一个文件里面存储了一个班级学生的信息,
            每个学生的信息占一行,
            格式为:姓名-性别-年龄
            要求通过程序实现随机点名器
        运行效果:
            70%概率随机到男生
            30%概率随机到女生
            总共随机100万次,统计结果
            观察结果是否满足
         */

        //定义字符缓冲流对象读取数据
        BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\infos.txt"));

        //定义集合存储数据
        ArrayList<String> infosList = new ArrayList<>();
        //存储数据
        String str;
        while((str = br.readLine()) != null) {
            infosList.add(str);
        }
        br.close();

        //定义变量统计输出的男生女生个数
        int boyCount = 0;
        int girlCount = 0;

        for (int i = 0; i < 1000000; i++) {
            //使用随机数控制概率
            Random r = new Random();
            //0代表女生 1代表男生
            int[] arr = {0,0,0,1,1,1,1,1,1,1};
            int randomIndex = r.nextInt(10);
            int gender = arr[randomIndex];

            //根据随机到的性别输出姓名
            int index = r.nextInt(infosList.size());
            String randomInfo = infosList.get(index);
            if(gender == 1) {
                //男生
                boyCount++;
                while (!("男".equals(randomInfo.split("-")[1]))) {
                    int newIndex = r.nextInt(infosList.size());
                    randomInfo = infosList.get(newIndex);
                }
            } else {
                //女生
                girlCount++;
                while (!("女".equals(randomInfo.split("-")[1]))) {
                    int newIndex = r.nextInt(infosList.size());
                    randomInfo = infosList.get(newIndex);
                }
            }
            System.out.println(randomInfo);
        }
        System.out.println(boyCount);
        System.out.println(girlCount);
    }
}
运行结果:

需求三:

随机点名器3
需求:
    有一个文件里面存储了一个班级学生的信息,
    每个学生的信息占一行,
    格式为:姓名-性别-年龄
    要求通过程序实现随机点名器
    第三次必定是张三同学
运行效果:
    第一次运行程序:随机姓名1(只显示名字)
    第二次运行程序:随机姓名2(只显示名字)
    第三次运行程序:张三
    ...
代码演示:
public class Test2_3 {
    public static void main(String[] args) throws IOException {
        /*
        随机点名器3
        需求:
            有一个文件里面存储了一个班级学生的信息,
            每个学生的信息占一行,
            格式为:姓名-性别-年龄
            要求通过程序实现随机点名器
            第三次必定是张三同学
        运行效果:
            第一次运行程序:随机姓名1(只显示名字)
            第二次运行程序:随机姓名2(只显示名字)
            第三次运行程序:张三
            ...
         */

        //创建存储信息文件对象
        File file = new File("C:\\Users\\Han\\IdeaProjects\\myio\\infos.txt");

        //将学生信息拷贝到集合中
        //创建字符缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader(file));
        //创建集合存储所有学生信息
        ArrayList<String> infosList = new ArrayList<>();
        //拷贝
        String str;
        while((str = br.readLine()) != null) {
            infosList.add(str);
        }
        br.close();

        //随机点名
        //添加第几次运行程序信息
        //读取文档内数字(初始存放0)
        FileReader fr = new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt");
        int chInt = fr.read();
        fr.close();
        chInt++;
        char ch = (char) chInt;
        if(ch == '3') {
            System.out.println("第3次运行程序:张三");
        } else {
            //打乱集合 获取随机姓名
            Collections.shuffle(infosList);
            String name = infosList.get(0).split("-")[0];
            System.out.println("第" + ch + "次运行程序:" + name);
        }
        //写入运行次数
        FileWriter fw = new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt");
        fw.write(chInt);
        fw.close();
    }
}
运行结果:

第一次:

第二次:

第三次:

第四次:

需求四:

随机点名器4
需求:
    有一个文件里面存储了一个班级学生的信息,
    每个学生的信息占一行,
    格式为:姓名-性别-年龄
    要求通过程序实现随机点名器
运行效果:
    被点到的学生不会再被点到
    如果班级中所有的学生都点完了,需要自动的重新开启第二轮点名
    细节1:假设班级有10个学生,每一轮中每一位学生只能被点到一次,程序运行10次,第一轮结束
    细节2:第11次运行的时候,我们不需要自己手动操作本地文件,要求程序自动开始第二轮点名

        创建一个新的文本文件infos2.txt只存储5个学生信息,便于展示

        

代码演示:
public class Test2_4 {
    public static void main(String[] args) throws IOException {
        /*
        随机点名器4
        需求:
            有一个文件里面存储了一个班级学生的信息,
            每个学生的信息占一行,
            格式为:姓名-性别-年龄
            要求通过程序实现随机点名器
        运行效果:
            被点到的学生不会再被点到
            如果班级中所有的学生都点完了,需要自动的重新开启第二轮点名
            细节1:假设班级有10个学生,每一轮中每一位学生只能被点到一次,程序运行10次,第一轮结束
            细节2:第11次运行的时候,我们不需要自己手动操作本地文件,要求程序自动开始第二轮点名
         */

        //先将信息存到集合内
        BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\infos2.txt"));
        ArrayList<String> infosList = new ArrayList<>();
        String str;
        while((str = br.readLine()) != null) {
            infosList.add(str);
        }
        br.close();

        //创建新集合存储未被点名的信息
        File src = new File("C:\\Users\\Han\\IdeaProjects\\myio\\srcinfos2.txt");
        src.createNewFile();
        //打乱集合写入新集合
        //打乱原集合
        Collections.shuffle(infosList);
        //创建集合存储未被点的信息
        //使用LinkedList因为增删快,查询首位部元素也快
        LinkedList<String> selectList = new LinkedList<>();


        //添加第几次运行程序信息
        //读取文档内数字(初始存放0)
        FileReader fr = new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt");
        int chInt = fr.read();
        fr.close();
        chInt++;
        char ch = (char) chInt;

        //随机点名
        BufferedReader br2 = new BufferedReader(new FileReader(src));
        if(src.length() == 0) {
            //文件为空 一轮点名全部点完
            //打乱集合
            Collections.shuffle(infosList);
            //写到文件中
            FileUtil.writeLines(infosList,src,"UTF-8");
            /*BufferedWriter bw2 = new BufferedWriter(new FileWriter(src));
            for (String info : infosList) {
                bw2.write(info);
                bw2.newLine();
            }
            bw2.close();*/
            //再点一次名
            selectName(br2, selectList, src, ch);

        } else {
            //文件不为空 直接点名并删除对应信息
            //点名
            selectName(br2, selectList, src, ch);
        }
        br2.close();

        //将运行次数写入文件
        FileWriter fw = new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\count.txt");
        fw.write(ch);
        fw.close();
    }

    //定义方法点名
    private static void selectName(BufferedReader br2, LinkedList<String> selectList, File src, char ch) throws IOException {
        //读取文件
        String str;
        while((str = br2.readLine()) != null) {
            selectList.add(str);
        }
        //得到姓名
        String name = selectList.get(0).split("-")[0];
        //删除元素
        selectList.remove(0);
        //写回文件
        FileUtil.writeLines(selectList, src,"UTF-8");
        //输出
        System.out.println("第" + ch + "次运行程序:" + name);
    }
}
运行结果:

第一次:

第二次:

第三次:

第四次:

第五次:

第六次:

★需求五:

txt文件中事先准备好10个学生信息,每个学生的信息独占一行
要求:每次被点到的学生,再次被点到的概率在原先的基础上降低一般
举例:点名3次,每次都点到xx,概率变化情况如下:
    第一次每人概率:10%
    第二次xx概率:5%   其他人概率:((100-5)/9)%
    第三次xx概率:2.5%    其他人概率:((100-2.5)/9)%

        先创建一个文本文件stuinfos.txt,存储下列信息

        

代码演示:

        为便于管理,定义Student类

Student类:
public class Student {
    private String name;
    private String gender;
    private int age;
    private double weight;


    public Student() {
    }

    public Student(String name, String gender, int age, double weight) {
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.weight = weight;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return weight
     */
    public double getWeight() {
        return weight;
    }

    /**
     * 设置
     * @param weight
     */
    public void setWeight(double weight) {
        this.weight = weight;
    }

    public String toString() {
        return "Student{name = " + name + ", gender = " + gender + ", age = " + age + ", weight = " + weight + "}";
    }
}
测试类:
public class Test2_5 {
    public static void main(String[] args) throws IOException {
        /*
        txt文件中事先准备好10个学生信息,每个学生的信息独占一行
        要求:每次被点到的学生,再次被点到的概率在原先的基础上降低一般
        举例:点名3次,每次都点到xx,概率变化情况如下:
            第一次每人概率:10%
            第二次xx概率:5%   其他人概率:((100-5)/9)%
            第三次xx概率:2.5%    其他人概率:((100-2.5)/9)%
         */

        //读取数据存入集合
        BufferedReader br = new BufferedReader(
                new FileReader("C:\\Users\\Han\\IdeaProjects\\myio\\stuinfos"));
        ArrayList<Student> stuList = new ArrayList<>();
        String line;
        while ((line = br.readLine()) != null) {
            String[] arr = line.split("-");
            stuList.add(new Student(arr[0], arr[1], Integer.parseInt(arr[2]), Double.parseDouble(arr[3])));
        }
        br.close();

        //计算权重总和
        double weight = 0;
        for (Student stu : stuList) {
            weight = weight + stu.getWeight();
        }

        //计算每个人的随即占比
        int len = stuList.size();
        double[] arr = new double[len];
        int index = 0;
        for (Student stu : stuList) {
            arr[index++] = stu.getWeight() / weight;
        }

        //计算每个人的权重区间
        for (int i = 1; i < arr.length; i++) {
            arr[i] = arr[i] + arr[i - 1];
        }

        //生成随机数并查找对应权重的信息
        double random = Math.random();
        int infoIndex = -Arrays.binarySearch(arr, random) - 1;// 方法返回:-插入点-1
        String name = stuList.get(infoIndex).getName();
        System.out.println(name);


        //改写数据
        //被点到学生的权重
        double selectedWeight = stuList.get(infoIndex).getWeight();
        for (int i = 0; i < stuList.size(); i++) {
            Student stu = stuList.get(i);
            if(i == infoIndex) {
                //被点到的学生,权重除2
                stu.setWeight(selectedWeight / 2);
            } else {
                //没被点到的学生,均分被点到学生减少的权重
                stu.setWeight(stu.getWeight() + (selectedWeight / 2) / (len - 1));
            }
        }

        //写回文档
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\Han\\IdeaProjects\\myio\\stuinfos"));
        for (Student stu : stuList) {
            StringBuilder sb = new StringBuilder();
            sb.append(stu.getName()).append("-").append(stu.getGender()).append("-").append(stu.getAge()).append("-").append(stu.getWeight());
            String str = sb.toString();
            bw.write(str);
            bw.newLine();
        }
        bw.close();
    }
}
运行结果:
第一次:

第二次:

  • 37
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值