菜鸟之路Day18一一IO流综合练习
作者:blue
时间:2025.2.21
文章目录
0.概述
文章内容学习自黑马程序员BV1yW4y1Y7Ms
1.生成假数据(网页爬虫)
实现了一个姓名生成器,主要功能是从指定网页爬取姓氏、男孩名和女孩名数据,处理后生成随机组合的姓名,并附加性别和年龄信息,最终将结果写入文件。
代码通过爬虫、正则匹配和随机化处理,生成包含姓氏、性别和年龄的模拟数据,适用于需要批量生成测试姓名的场景。
package FinallyTest;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FinallyTest1 {
public static void main(String[] args) throws IOException {
//https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d
//http://www.haoming8.cn/baobao/10881.html
//http://www.haoming8.cn/baobao/7641.html
//1.创建字符串表示网址
String FamilyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";
String BoyNameNet = "http://www.haoming8.cn/baobao/10881.html";
String GirlNameNet = "http://www.haoming8.cn/baobao/7641.html";
//2.爬取网页上的信息
//仅仅爬取出了前端源码
String FamilyNameInfo = webCrawler(FamilyNameNet);
String BoyNameInfo = webCrawler(BoyNameNet);
String GirlNameInfo = webCrawler(GirlNameNet);
//3.获取真正想要的信息
ArrayList<String> FamilyNameTempList = getData(FamilyNameInfo, "(.{4})(,|。)", 1);
FamilyNameTempList.removeIf(e -> e.equals("em\">"));//去除掉造成干扰的标签
ArrayList<String> BoyNameTempList = getData(BoyNameInfo, "([\\u4E00-\\u9FA5]{2})(、|。)", 1);
ArrayList<String> GirlNameTempList = getData(GirlNameInfo, "(.. ){4}..", 0);
//4.处理数据
//FamilyNameTempList(姓氏)
//处理方法:获取单个姓氏
ArrayList<String> FamilyNameList = new ArrayList<>();
for (String s : FamilyNameTempList) {
for (int i = 0; i < s.length(); i++) {
FamilyNameList.add(s.charAt(i) + "");
}
}
//BoyNameTempList(男生名字)
//处理方法:去重
ArrayList<String> BoyNameList = new ArrayList<>();
for (String s : BoyNameTempList) {
if (!BoyNameList.contains(s)) {
BoyNameList.add(s);
}
}
//GirlNameTempList(女生名字)
//处理方法:切割
ArrayList<String> GirlNameList = new ArrayList<>();
for (String s : GirlNameTempList) {
String[] s1 = s.split(" ");
for (String string : s1) {
GirlNameList.add(string);
}
}
//5.组合所有姓名
//格式:姓名(唯一)-性别-年龄
ArrayList<String> list = getInfos(FamilyNameList, BoyNameList, GirlNameList, 50, 70);
Collections.shuffle(list);
System.out.println(list);
//6.将数据写到文件中
//这里用BufferedWriter,主要是为了使用其中的newLine方法
BufferedWriter bw = new BufferedWriter(new FileWriter("src\\FinallyTest\\name.txt"));
for (String name : list) {
bw.write(name);
bw.newLine();
}
bw.close();
}
/*
* 作用:组合姓名(唯一)-性别-年龄
* 参数一:姓氏集合
* 参数二:男生名字集合
* 参数三:女生名字集合
* 参数四:男生个数
* 参数五:女生个数
* */
private static ArrayList<String> getInfos(ArrayList<String> FamilyNameList, ArrayList<String> BoyNameList, ArrayList<String> GirlNameList, int boyCount, int girlCount) {
//用set来做一个去重操作
//男生姓名
HashSet<String> boyhs = new HashSet<>();
while (true) {
if (boyhs.size() == boyCount) {
break;
} else {
//每次打乱之后再拼接
Collections.shuffle(FamilyNameList);
Collections.shuffle(BoyNameList);
boyhs.add(FamilyNameList.getFirst() + BoyNameList.getFirst());//取第一个拼接
}
}
//女生姓名
HashSet<String> girlhs = new HashSet<>();
while (true) {
if (girlhs.size() == girlCount) {
break;
} else {
//每次打乱之后再拼接
Collections.shuffle(FamilyNameList);
Collections.shuffle(GirlNameList);
girlhs.add(FamilyNameList.getFirst() + GirlNameList.getFirst());//取第一个拼接
}
}
ArrayList<String> list = new ArrayList<>();//存储返回值
//[18-27岁]
Random rd = new Random();
for (String boyName : boyhs) {
int age = rd.nextInt(10)+18;
list.add(boyName+"-男-"+age);
}
for (String girlName : girlhs) {
int age = rd.nextInt(10)+18;
list.add(girlName+"-女-"+age);
}
return list;
}
/*
* 获取真正想要的数据
* 参数一:
* 数据源
* 参数二:
* 正则表达式
* 参数三:
* 真正想要的数据
* */
private static ArrayList<String> getData(String str, String regex, int index) {
ArrayList<String> list = new ArrayList<>();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
list.add(matcher.group(index));
}
return list;
}
//爬取整个网页的信息
private static String webCrawler(String familyNameNet) throws IOException {
//用StringBuilder对象来获取拼接的数据
StringBuilder sb = new StringBuilder();
//创建url对象
URL url = new URL(familyNameNet);
//链接到网页
URLConnection conn = url.openConnection();
//获取网页中的数据,conn中有getInputStream方法可以获取字节流,但是有可能涉及到爬取中文数据
//所以我们用转换流把其包装为字符流
InputStreamReader isr = new InputStreamReader(conn.getInputStream());
int ch;
while ((ch = isr.read()) != -1) {
sb.append((char) ch);
}
//释放资源
isr.close();
return sb.toString();
}
}
利用hutool包中的方法来尝试实现上述功能
hutool包参考文档中的代码,与我们的源代码非常接近。
//请求列表页
String listContent = HttpUtil.get("https://www.oschina.net/action/ajax/get_more_news_list?newsType=&p=2");
//使用正则获取所有标题
List<String> titles = ReUtil.findAll("<span class=\"text-ellipsis\">(.*?)</span>", listContent, 1);
for (String title : titles) {
//打印标题
Console.log(title);
}
代码实现:
package FinallyTest;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HttpUtil;
import java.util.*;
public class FinallyTest2 {
public static void main(String[] args) {
//1.创建字符串表示网址
String FamilyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";
String BoyNameNet = "http://www.haoming8.cn/baobao/10881.html";
String GirlNameNet = "http://www.haoming8.cn/baobao/7641.html";
//2.爬取网页上的信息(请求列表页)
String FamilyNameInfo = HttpUtil.get(FamilyNameNet);
String BoyNameInfo = HttpUtil.get(BoyNameNet);
String GirlNameInfo = HttpUtil.get(GirlNameNet);
//3.获取真正想要的信息(使用正则获取所有标题)
List<String> FamilyNameTempList = ReUtil.findAll("(.{4})(,|。)", FamilyNameInfo, 1);
FamilyNameTempList.removeIf(e -> e.equals("em\">"));//去除掉造成干扰的标签
List<String> BoyNameTempList = ReUtil.findAll("([\\u4E00-\\u9FA5]{2})(、|。)", BoyNameInfo, 1);
List<String> GirlNameTempList = ReUtil.findAll("(.. ){4}..", GirlNameInfo, 0);
//4.处理数据
//FamilyNameTempList(姓氏)
//处理方法:获取单个姓氏
ArrayList<String> FamilyNameList = new ArrayList<>();
for (String s : FamilyNameTempList) {
for (int i = 0; i < s.length(); i++) {
FamilyNameList.add(s.charAt(i) + "");
}
}
//BoyNameTempList(男生名字)
//处理方法:去重
ArrayList<String> BoyNameList = new ArrayList<>();
for (String s : BoyNameTempList) {
if (!BoyNameList.contains(s)) {
BoyNameList.add(s);
}
}
//GirlNameTempList(女生名字)
//处理方法:切割
ArrayList<String> GirlNameList = new ArrayList<>();
for (String s : GirlNameTempList) {
String[] s1 = s.split(" ");
for (String string : s1) {
GirlNameList.add(string);
}
}
//5.组合所有姓名
//格式:姓名(唯一)-性别-年龄
ArrayList<String> list = getInfos(FamilyNameList, BoyNameList, GirlNameList, 50, 70);
Collections.shuffle(list);
System.out.println(list);
//6.将数据写到文件中
FileUtil.writeLines(list, "FinallyTest\\namesHutool.txt", "UTF-8");
}
/*
* 作用:组合姓名(唯一)-性别-年龄
* 参数一:姓氏集合
* 参数二:男生名字集合
* 参数三:女生名字集合
* 参数四:男生个数
* 参数五:女生个数
* */
private static ArrayList<String> getInfos(ArrayList<String> FamilyNameList, ArrayList<String> BoyNameList, ArrayList<String> GirlNameList, int boyCount, int girlCount) {
//用set来做一个去重操作
//男生姓名
HashSet<String> boyhs = new HashSet<>();
while (true) {
if (boyhs.size() == boyCount) {
break;
} else {
//每次打乱之后再拼接
Collections.shuffle(FamilyNameList);
Collections.shuffle(BoyNameList);
boyhs.add(FamilyNameList.getFirst() + BoyNameList.getFirst());//取第一个拼接
}
}
//女生姓名
HashSet<String> girlhs = new HashSet<>();
while (true) {
if (girlhs.size() == girlCount) {
break;
} else {
//每次打乱之后再拼接
Collections.shuffle(FamilyNameList);
Collections.shuffle(GirlNameList);
girlhs.add(FamilyNameList.getFirst() + GirlNameList.getFirst());//取第一个拼接
}
}
ArrayList<String> list = new ArrayList<>();//存储返回值
//[18-27岁]
Random rd = new Random();
for (String boyName : boyhs) {
int age = rd.nextInt(10)+18;
list.add(boyName+"-男-"+age);
}
for (String girlName : girlhs) {
int age = rd.nextInt(10)+18;
list.add(girlName+"-女-"+age);
}
return list;
}
}
2.随机点名器
2.1随机点名器1
需求:
有一个文件里面存储了班级同学的信息,每一个信息占一行。
格式为:张三-男-23
要求通过程序实现随机点名器。
运行效果:
第一次运行程序:随机同学姓名1(只显示名字)
第二次运行程序:随机同学姓名2(只显示名字)
第三次运行程序:随机同学姓名3(只显示名字)
public class FinallyTest4 {
public static void main(String[] args) throws IOException {
//1.先将文件中的数据读入到集合中
ArrayList<String> list = getData("src\\FinallyTest\\name.txt");
//2.打乱,根据
Collections.shuffle(list);
String[] arr = list.getFirst().split("-");
System.out.println(arr[0]);
}
private static ArrayList<String> getData(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
ArrayList<String> list = new ArrayList<>();
String elem;
while((elem=br.readLine())!=null){
list.add(elem);
}
br.close();
return list;
}
}
2.2随机点名器2
需求:
一个文件里面存储了班级同学的信息,每一个学生信息占一行格式为:张三-男-23。
要求通过程序实现随机点名器,
运行效果:
70%的概率随机到男生
30%的概率随机到女生
总共随机100万次,统计结果。
注意观察:看生成男生和女生的比例是不是接近于7:3
package FinallyTest;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;
public class FinallyTest4 {
public static void main(String[] args) throws IOException {
//1.先将文件中的数据读入到集合中
ArrayList<String> list = getData("src\\FinallyTest\\name.txt");
//2.将男女集合进行分类
ArrayList<String> BoyList = new ArrayList<>();
ArrayList<String> GirlList = new ArrayList<>();
for (String name : list) {
String[] arr = name.split("-");
if(arr[1].equals("男")){
BoyList.add(name);
}
else {
GirlList.add(name);
}
}
//3.创建一个数组,10个数,7个1,3个0,1代表抽男生,0代表抽女生,模拟概率
ArrayList<Integer> arr = new ArrayList<>();
Collections.addAll(arr,1,1,1,1,1,1,1,0,0,0);
//4.统计次数
int BoyCount=0;
int GirlCount=0;
for(int i=1;i<=1000000;i++){
Collections.shuffle(arr);
//抽男生
if(arr.getFirst()==1){
Collections.shuffle(BoyList);
BoyCount++;
System.out.println(BoyList.getFirst());
}
else {//抽女生
Collections.shuffle(GirlList);
GirlCount++;
System.out.println(GirlList.getFirst());
}
}
System.out.println(BoyCount/1000000.0+"");//0.699904
System.out.println(GirlCount/1000000.0+"");//0.300096
}
private static ArrayList<String> getData(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
ArrayList<String> list = new ArrayList<>();
String elem;
while((elem=br.readLine())!=null){
list.add(elem);
}
br.close();
return list;
}
}
2.3随机点名器3
需求:
一个文件里面存储了班级同学的姓名,每一个姓名占一行。
要求通过程序实现随机点名器
第三次必定是张三同学
运行效果:
第一次运行程序:随机同学姓名1
第二次运行程序:随机同学姓名2
第三次运行程序:张三
思路:新建一个Count文件来记录程序运行的次数
package FinallyTest;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
public class FinallyTest5 {
public static void main(String[] args) throws IOException {
//1.先将文件中的数据读入到集合中
ArrayList<String> list = getData("src\\FinallyTest\\name.txt");
BufferedReader br = new BufferedReader(new FileReader("src\\FinallyTest\\Count.txt"));
int count = br.read()-'0';
BufferedWriter bw = new BufferedWriter(new FileWriter("src\\FinallyTest\\Count.txt"));
if(count==2) {
System.out.println("张三");
bw.write(count+1+"");
}
else {
Collections.shuffle(list);
String[] arr = list.getFirst().split("-");
System.out.println(arr[0]);
bw.write(count+1+"");
}
bw.close();
br.close();
}
private static ArrayList<String> getData(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
ArrayList<String> list = new ArrayList<>();
String elem;
while((elem=br.readLine())!=null){
list.add(elem);
}
br.close();
return list;
}
}
2.4随机点名器4
需求:
一个文件里面存储了班级同学的姓名,每一个姓名占一行。
要求通过程序实现随机点名器。
运行效果:
被点到的学生不会再被点到
如果班级中所有的学生都点完了,需要自动的重新开启第二轮点名
细节1:假设班级有10个学生,每一轮中每一位学生只能被点到一次,程序运行10次,第一轮结束
细节2:第11次运行的时候,我们自己不需要手动操作本地文件,要求程序自动开始第二轮点名
思路:新建一个文件记录被点到学生的姓名,原先的文件点到一个删一个,这样就保证了不重复,删完了,就交替两个文件中的数据,代表新一轮开始
public class FinallyTest6 {
public static void main(String[] args) throws IOException, InterruptedException {
for(int i=0;i<=120;i++){
runMain();
Thread.sleep(500);//暂停0.5秒查看文件效果
}
}
private static ArrayList<String> getData(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
ArrayList<String> list = new ArrayList<>();
String elem;
while((elem=br.readLine())!=null){
list.add(elem);
}
br.close();
return list;
}
private static void runMain() throws IOException {
//1.获取数据
ArrayList<String> list1 = getData("src\\FinallyTest\\name.txt");
ArrayList<String> list2 = getData("src\\FinallyTest\\namecopy.txt");
//2.打乱,点名,点一个删一个
if(list1.isEmpty()){//交替
list1.addAll(list2);
list2.clear();
}
Collections.shuffle(list1);
System.out.println(list1.getFirst());
list2.add(list1.getFirst());
list1.removeFirst();
//3.写回
BufferedWriter bw1 = new BufferedWriter(new FileWriter("src\\FinallyTest\\name.txt"));
BufferedWriter bw2 = new BufferedWriter(new FileWriter("src\\FinallyTest\\namecopy.txt"));
for (String name : list1) {
bw1.write(name);
bw1.newLine();
}
for (String name : list2) {
bw2.write(name);
bw2.newLine();
}
bw1.close();
bw2.close();
}
}
2.5随机点名器5(带权重的随机算法)
需求:txt文件中事先准备好一些学生信息,每个学生的信息独占一行
要求1:每次被点到的学生,再次被点到的概率在原先的基础上降低一半。
思路:首先,我们可以先处理数据,在数据中先给每个数据都添加上权重
曾博月-男-26-1
赫依娜-女-26-1
杜茜燕-女-26-1
正美丽-男-25-1
蒋胜游-男-18-1
郈蔼雅-女-26-1
连雅宁-女-24-1
于淑弘-女-23-1
宗紫凡-女-23-1
闵红新-女-27-1
蒙玉涛-男-21-1
............
创建Student类来管理学生对象
package FinallyTest;
public class Student {
private String name;
private String sex;
private int age;
private double weight;
public Student() {
}
public Student(String name, String sex, int age, double weight) {
this.name = name;
this.sex = sex;
this.age = age;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
//改写了toString方法,方便写回到文件中
@Override
public String toString() {
return name + "-" + sex + "-" + age + "-" + weight;
}
}
带权重的随机过程
package FinallyTest;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
public class FinallyTest7 {
public static void main(String[] args) throws IOException {
//1.获取数据
ArrayList<String> list = getData("src\\FinallyTest\\name_weight.txt");
//2.处理数据
ArrayList<Student> listStu = new ArrayList<>();
for (String info : list) {
//切割
String[] arr = info.split("-");
//存入到listStu中
listStu.add(new Student(arr[0], arr[1], Integer.parseInt(arr[2]), Double.parseDouble(arr[3])));
}
//3.计算权重总和
double weight_sum = 0;
for (Student student : listStu) {
weight_sum = weight_sum + student.getWeight();
}
//4.计算每个人在总权重中的实际占比
double[] listRate = new double[listStu.size()];
int index = 0;
for (Student student : listStu) {
listRate[index] = (student.getWeight() / weight_sum);
index++;
}
//5.计算每个人权重的占比范围
for (int i = 1; i < listRate.length; i++) {
listRate[i] = listRate[i - 1] + listRate[i];
}
//6.随机抽取
double x = Math.random(); //随机一个0-1的小数
//binarySearch的结果 = -插入点-1
//插入点 = -binarySearch的结果-1
//index1即为插入点
int index1 = -Arrays.binarySearch(listRate, x) - 1;//二分查找插入点
System.out.println("本轮抽取到的人为:" + listStu.get(index1).getName());//被抽取到的人
//7.修改权重
double weight = listStu.get(index1).getWeight()/2;
listStu.get(index1).setWeight(weight);
//8.将listStu中的数值写回到文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("src\\FinallyTest\\name_weight.txt"));
for (Student student : listStu) {
bw.write(student.toString());
bw.newLine();//换行
}
bw.close();
}
//读取文件中的数据
private static ArrayList<String> getData(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
ArrayList<String> list = new ArrayList<>();
String elem;
while ((elem = br.readLine()) != null) {
list.add(elem);
}
br.close();
return list;
}
}