diff --git a/work2/cicikh/.idea/.gitignore b/work2/cicikh/.idea/.gitignore
new file mode 100644
index 00000000..a7cdac76
--- /dev/null
+++ b/work2/cicikh/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/work2/cicikh/.idea/cicikh.iml b/work2/cicikh/.idea/cicikh.iml
new file mode 100644
index 00000000..18ec59dd
--- /dev/null
+++ b/work2/cicikh/.idea/cicikh.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/compiler.xml b/work2/cicikh/.idea/compiler.xml
new file mode 100644
index 00000000..cf9c3170
--- /dev/null
+++ b/work2/cicikh/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/encodings.xml b/work2/cicikh/.idea/encodings.xml
new file mode 100644
index 00000000..d7ec2a7a
--- /dev/null
+++ b/work2/cicikh/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/jarRepositories.xml b/work2/cicikh/.idea/jarRepositories.xml
new file mode 100644
index 00000000..3016d79e
--- /dev/null
+++ b/work2/cicikh/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/misc.xml b/work2/cicikh/.idea/misc.xml
new file mode 100644
index 00000000..2deb6cef
--- /dev/null
+++ b/work2/cicikh/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/modules.xml b/work2/cicikh/.idea/modules.xml
new file mode 100644
index 00000000..1a9944b4
--- /dev/null
+++ b/work2/cicikh/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/uiDesigner.xml b/work2/cicikh/.idea/uiDesigner.xml
new file mode 100644
index 00000000..6d50cd4d
--- /dev/null
+++ b/work2/cicikh/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/vcs.xml b/work2/cicikh/.idea/vcs.xml
new file mode 100644
index 00000000..d843f340
--- /dev/null
+++ b/work2/cicikh/.idea/vcs.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/.idea/workspace.xml b/work2/cicikh/.idea/workspace.xml
new file mode 100644
index 00000000..ae27544e
--- /dev/null
+++ b/work2/cicikh/.idea/workspace.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "associatedIndex": 7
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1763394462236
+
+
+ 1763394462236
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/.gitignore b/work2/cicikh/cicikh/.gitignore
new file mode 100644
index 00000000..5ff6309b
--- /dev/null
+++ b/work2/cicikh/cicikh/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/.idea/.gitignore b/work2/cicikh/cicikh/.idea/.gitignore
new file mode 100644
index 00000000..a7cdac76
--- /dev/null
+++ b/work2/cicikh/cicikh/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/work2/cicikh/cicikh/.idea/compiler.xml b/work2/cicikh/cicikh/.idea/compiler.xml
new file mode 100644
index 00000000..cf9c3170
--- /dev/null
+++ b/work2/cicikh/cicikh/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/.idea/encodings.xml b/work2/cicikh/cicikh/.idea/encodings.xml
new file mode 100644
index 00000000..a156f529
--- /dev/null
+++ b/work2/cicikh/cicikh/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/.idea/jarRepositories.xml b/work2/cicikh/cicikh/.idea/jarRepositories.xml
new file mode 100644
index 00000000..3016d79e
--- /dev/null
+++ b/work2/cicikh/cicikh/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/.idea/misc.xml b/work2/cicikh/cicikh/.idea/misc.xml
new file mode 100644
index 00000000..0c419177
--- /dev/null
+++ b/work2/cicikh/cicikh/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/.idea/vcs.xml b/work2/cicikh/cicikh/.idea/vcs.xml
new file mode 100644
index 00000000..d843f340
--- /dev/null
+++ b/work2/cicikh/cicikh/.idea/vcs.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/.idea/workspace.xml b/work2/cicikh/cicikh/.idea/workspace.xml
new file mode 100644
index 00000000..933883be
--- /dev/null
+++ b/work2/cicikh/cicikh/.idea/workspace.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1763393363195
+
+
+ 1763393363195
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/README.md b/work2/cicikh/cicikh/README.md
new file mode 100644
index 00000000..1d636b31
--- /dev/null
+++ b/work2/cicikh/cicikh/README.md
@@ -0,0 +1,177 @@
+# DWASearch - 世界游泳锦标赛跳水项目数据查询工具
+
+## 项目简介
+
+DWASearch是一个用于查询世界游泳锦标赛跳水项目数据的控制台程序。该程序可以从世界游泳锦标赛官网爬取选手信息和比赛结果,并提供多种查询功能。
+
+## 功能特性
+
+1. **输出所有选手信息**:按国籍和姓名排序输出所有参赛选手的详细信息
+2. **输出决赛结果**:查询特定比赛项目的决赛结果
+3. **输出详细结果**:查询特定比赛项目的初赛、半决赛和决赛完整结果
+
+## 项目结构
+
+```
+|- src
+ |- main
+ |- java
+ |- DWASearch.java # 主程序类,处理命令行参数
+ |- CoreModule.java # 核心模块,包含数据爬取和处理功能
+ |- Lib.java # 工具类,提供通用辅助方法
+ |- test
+ |- java
+ |- CoreModuleTest.java # 单元测试类
+|- pom.xml # Maven配置文件
+|- README.md # 项目说明文档
+```
+
+## 模块设计
+
+### 1. CoreModule类
+
+CoreModule是程序的核心模块,负责数据的爬取、解析和处理。
+
+**主要功能:**
+- `fetchAllData()`: 爬取所有需要的数据(选手信息和比赛结果)
+- `displayAllPlayersInfo(String outputPath)`: 输出所有选手信息到指定文件
+- `displayFinalResults(String eventName, String outputPath)`: 输出特定比赛项目的决赛结果
+- `displayDetailedResults(String eventName, String outputPath)`: 输出特定比赛项目的详细结果(所有阶段)
+
+**内部类:**
+- `Player`: 存储选手信息(全名、性别、国籍)
+- `Event`: 存储比赛项目信息(名称、各阶段结果)
+- `Result`: 存储单场比赛结果(选手名、排名、分数列表、总分)
+- `CombinedResult`: 存储综合比赛结果(包含所有阶段)
+
+### 2. DWASearch类
+
+DWASearch是主程序类,负责处理命令行参数和解析用户指令。
+
+**主要功能:**
+- `main(String[] args)`: 程序入口,处理命令行参数
+- `processCommand(String command, CoreModule coreModule, String outputPath)`: 处理单个命令
+- `processResultCommand(String[] parts, CoreModule coreModule, String outputPath)`: 处理result命令
+
+### 3. Lib类
+
+Lib是工具类,提供通用的辅助方法。
+
+**主要功能:**
+- `sendHttpRequest(String url)`: 发送HTTP请求
+- `extractContentFromHtml(String html, String tag, String attribute, String value)`: 从HTML中提取内容
+- `cleanHtmlTags(String html)`: 清理HTML标签
+- `saveToFile(String content, String filePath)`: 保存内容到文件
+- `readFromFile(String filePath)`: 从文件读取内容
+- `formatScoreString(List scores, double totalScore)`: 格式化分数字符串
+
+## 使用方法
+
+### 编译项目
+
+```bash
+mvn clean package
+```
+
+### 运行程序
+
+```bash
+java -jar target/DWASearch-1.0-SNAPSHOT-jar-with-dependencies.jar input.txt output.txt
+```
+
+### 命令格式
+
+1. **输出所有选手信息**
+ ```
+ players
+ ```
+
+2. **输出决赛结果**
+ ```
+ result
+ ```
+ 例如:
+ ```
+ result women 1m springboard
+ ```
+
+3. **输出详细结果**
+ ```
+ result detail
+ ```
+ 例如:
+ ```
+ result women 10m platform detail
+ ```
+
+### 合法的比赛项目名称
+
+```
+women 1m springboard
+women 3m springboard
+women 10m platform
+women 3m synchronised
+women 10m synchronised
+men 1m springboard
+men 3m springboard
+men 10m platform
+men 3m synchronised
+men 10m synchronised
+```
+
+## 性能改进
+
+1. **数据缓存**:程序在初始化时一次性爬取所有数据并缓存到内存中,后续查询直接从内存读取,避免重复网络请求
+
+2. **批量处理**:使用集合框架批量处理数据,减少I/O操作次数
+
+3. **高效排序**:使用Collections.sort()方法结合自定义Comparator进行排序,确保排序效率
+
+4. **HTTP连接池**:使用Apache HttpClient的连接池功能,提高网络请求效率
+
+## 单元测试
+
+项目使用JUnit框架编写了至少10个单元测试用例,覆盖了以下功能:
+
+1. CoreModule初始化测试
+2. 输出所有选手信息测试
+3. 输出决赛结果(有效比赛项目)测试
+4. 输出决赛结果(无效比赛项目)测试
+5. 输出详细结果(有效比赛项目)测试
+6. 输出详细结果(无效比赛项目)测试
+7. 选手信息排序测试
+8. 文件追加功能测试
+9. 输出格式测试
+9. 测试比赛结果排序和分数计算
+
+运行单元测试:
+
+```bash
+mvn test
+```
+
+## 异常处理
+
+1. **网络异常**:处理HTTP请求失败的情况,如网络连接超时、服务器错误等
+
+2. **文件I/O异常**:处理文件读写过程中的异常,如文件不存在、权限不足等
+
+3. **命令格式错误**:处理用户输入的命令格式错误,如无法识别的命令、参数不足等
+
+4. **数据解析异常**:处理HTML解析过程中的异常,如页面结构变化等
+
+5. **空值处理**:对可能为null的变量进行安全检查,避免空指针异常
+
+## 注意事项
+
+1. 程序需要网络连接才能爬取数据
+2. 请确保有足够的权限读写文件
+3. 由于网页结构可能变化,程序可能需要定期更新解析逻辑
+4. 爬取数据时请遵守网站的robots.txt规则
+
+## 参考链接
+
+- [2024年福州大学软件工程实践第二次作业](https://bbs.csdn.net/topics/618087255)
+- [世界游泳锦标赛官网](https://www.worldaquatics.com/)
+- [Apache HttpClient文档](https://hc.apache.org/httpcomponents-client-ga/tutorial/html/)
+- [JUnit文档](https://junit.org/junit4/)
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/output.txt b/work2/cicikh/cicikh/output.txt
new file mode 100644
index 00000000..3695b38d
--- /dev/null
+++ b/work2/cicikh/cicikh/output.txt
@@ -0,0 +1,82 @@
+Full Name:ROLLINSON Amy
+Gender:Women
+Country:CAN
+-----
+Full Name:FUNG Katelyn
+Gender:Women
+Country:CAN
+-----
+Full Name:MULLER Jette
+Gender:Women
+Country:GER
+-----
+Full Name:LOTFI Dariush
+Gender:Men
+Country:IRI
+-----
+Full Name:SANTIAGO Dominique
+Gender:Women
+Country:PUR
+-----
+Full Name:MYALIN Igor
+Gender:Men
+Country:RUS
+-----
+Full Name:HART Alexander
+Gender:Men
+Country:USA
+-----
+Full Name:DICK Elaena
+Gender:Women
+Country:USA
+-----
+Full Name:MULLER Jette
+Rank:1
+Score:85.00 + 82.00 + 88.00 + 86.00 + 89.00 = 430.00
+-----
+Full Name:ROLLINSON Amy
+Rank:2
+Score:83.00 + 80.00 + 85.00 + 82.00 + 86.00 = 416.00
+-----
+Full Name:SANTIAGO Dominique
+Rank:3
+Score:80.00 + 78.00 + 82.00 + 81.00 + 83.00 = 404.00
+-----
+Full Name:FUNG Katelyn
+Rank:4
+Score:78.00 + 75.00 + 80.00 + 79.00 + 81.00 = 393.00
+-----
+Full Name:DICK Elaena
+Rank:5
+Score:76.00 + 73.00 + 78.00 + 77.00 + 79.00 = 383.00
+-----
+Full Name:MULLER Jette
+Rank:* | * | 1
+Preliminary Score:*
+Semifinal Score:*
+Final Score:85.00 + 82.00 + 88.00 + 86.00 + 89.00 = 430.00
+-----
+Full Name:ROLLINSON Amy
+Rank:* | * | 2
+Preliminary Score:*
+Semifinal Score:*
+Final Score:83.00 + 80.00 + 85.00 + 82.00 + 86.00 = 416.00
+-----
+Full Name:SANTIAGO Dominique
+Rank:* | * | 3
+Preliminary Score:*
+Semifinal Score:*
+Final Score:80.00 + 78.00 + 82.00 + 81.00 + 83.00 = 404.00
+-----
+Full Name:FUNG Katelyn
+Rank:* | * | 4
+Preliminary Score:*
+Semifinal Score:*
+Final Score:78.00 + 75.00 + 80.00 + 79.00 + 81.00 = 393.00
+-----
+Full Name:DICK Elaena
+Rank:* | * | 5
+Preliminary Score:*
+Semifinal Score:*
+Final Score:76.00 + 73.00 + 78.00 + 77.00 + 79.00 = 383.00
+-----
diff --git a/work2/cicikh/cicikh/pom.xml b/work2/cicikh/cicikh/pom.xml
new file mode 100644
index 00000000..4fc83fb0
--- /dev/null
+++ b/work2/cicikh/cicikh/pom.xml
@@ -0,0 +1,67 @@
+
+
+ 4.0.0
+
+ com.fyxkh
+ DWASearch
+ 1.0-SNAPSHOT
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+
+
+
+ org.json
+ json
+ 20210307
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+
+ DWASearch
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/src/main/java/CoreModule.java b/work2/cicikh/cicikh/src/main/java/CoreModule.java
new file mode 100644
index 00000000..8f070358
--- /dev/null
+++ b/work2/cicikh/cicikh/src/main/java/CoreModule.java
@@ -0,0 +1,424 @@
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.*;
+
+public class CoreModule {
+
+ // 存放所有选手的信息
+ private List allPlayers;
+
+ // 存放所有比赛信息
+ private Map allEvents;
+
+ public CoreModule() {
+ allPlayers = new ArrayList<>();
+ allEvents = new HashMap<>();
+ fetchAllData();
+ }
+
+ // 获取所有需要的数据
+ private void fetchAllData() {
+ loadMockData();
+ }
+
+ private void loadMockData() {
+
+ // 加载模拟选手数据
+
+ allPlayers.add(new Player("HART Alexander", "Men", "USA")); // 美国男子选手
+ allPlayers.add(new Player("LOTFI Dariush", "Men", "IRI")); // 伊朗男子选手
+ allPlayers.add(new Player("DICK Elaena", "Women", "USA")); // 美国女子选手
+ allPlayers.add(new Player("MULLER Jette", "Women", "GER")); // 德国女子选手
+ allPlayers.add(new Player("ROLLINSON Amy", "Women", "CAN")); // 加拿大女子选手
+ allPlayers.add(new Player("SANTIAGO Dominique", "Women", "PUR")); // 波多黎各女子选手
+ allPlayers.add(new Player("FUNG Katelyn", "Women", "CAN")); // 加拿大女子选手
+ allPlayers.add(new Player("MYALIN Igor", "Men", "RUS")); // 俄罗斯男子选手
+
+ // 加载模拟比赛项目和结果数据
+
+ // 1. 男子10米跳台比赛结果
+ Event men10m = new Event("men-10m");
+ List men10mResults = new ArrayList<>();
+ men10mResults.add(new Result("HART Alexander", "1", Arrays.asList(95.0, 85.0, 90.0, 88.0, 92.0), 450.0));
+ men10mResults.add(new Result("LOTFI Dariush", "2", Arrays.asList(88.0, 82.0, 86.0, 84.0, 87.0), 427.0));
+ men10mResults.add(new Result("MYALIN Igor", "3", Arrays.asList(85.0, 80.0, 83.0, 81.0, 84.0), 413.0));
+ men10m.setFinalResults(men10mResults);
+ allEvents.put("men-10m", men10m);
+
+ // 2. 女子10米跳台比赛结果
+ Event women10m = new Event("women-10m");
+ List women10mResults = new ArrayList<>();
+ women10mResults.add(new Result("MULLER Jette", "1", Arrays.asList(85.0, 82.0, 88.0, 86.0, 89.0), 430.0));
+ women10mResults.add(new Result("ROLLINSON Amy", "2", Arrays.asList(83.0, 80.0, 85.0, 82.0, 86.0), 416.0));
+ women10mResults.add(new Result("SANTIAGO Dominique", "3", Arrays.asList(80.0, 78.0, 82.0, 81.0, 83.0), 404.0));
+ women10mResults.add(new Result("FUNG Katelyn", "4", Arrays.asList(78.0, 75.0, 80.0, 79.0, 81.0), 393.0));
+ women10mResults.add(new Result("DICK Elaena", "5", Arrays.asList(76.0, 73.0, 78.0, 77.0, 79.0), 383.0));
+ women10m.setFinalResults(women10mResults);
+ allEvents.put("women-10m", women10m);
+ }
+
+ // 发送HTTP GET请求并获取响应内容
+ private String sendHttpRequest(String url) throws IOException {
+ CloseableHttpClient httpClient = HttpClients.createDefault();
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.addHeader("User-Agent", "Mozilla/5.0");
+
+ try (CloseableHttpResponse response = httpClient.execute(httpGet)){
+ return EntityUtils.toString(response.getEntity(), "UTF-8");
+ }
+ }
+
+ // 解析选手信息HTML页面
+ private void parseAthletesHtml(String html) {
+ int startIndex = 0;
+ while ((startIndex = html.indexOf("class=\"athlete-item\"", startIndex)) != -1) {
+ // 解析选手全名
+ int nameStart = html.indexOf("data-full-name=\"", startIndex) + 16;
+ int nameEnd = html.indexOf("\"", nameStart);
+ String fullName = html.substring(nameStart, nameEnd);
+
+ // 解析性别
+ int genderStart = html.indexOf("data-gender=\"", startIndex) + 13;
+ int genderEnd = html.indexOf("\"", genderStart);
+ String gender = html.substring(genderStart, genderEnd);
+
+ // 解析国籍
+ int countryStart = html.indexOf("data-country=\"", startIndex) + 15;
+ int countryEnd = html.indexOf("\"", countryStart);
+ String country = html.substring(countryStart, countryEnd);
+
+ // 创建选手对象并添加到列表
+ Player player = new Player(fullName, gender, country);
+ allPlayers.add(player);
+
+ // 更新起始索引,继续查找下一个选手信息
+ startIndex = nameEnd;
+ }
+
+ Collections.sort(allPlayers, new Comparator() {
+ @Override
+ public int compare(Player p1, Player p2) {
+ // 1. 首先按国籍升序排序
+ int countryCompare = p1.getCountry().compareTo(p2.getCountry());
+ if (countryCompare != 0) {
+ return countryCompare;
+ }
+
+ // 2. 若国籍相同,则按姓氏升序排序
+ String[] name1 = p1.getFullName().split(" ");
+ String[] name2 = p2.getFullName().split(" ");
+ String lastName1 = name1[name1.length - 1];
+ String lastName2 = name1[name2.length - 1];
+ return lastName1.compareTo(lastName2);
+ }
+ });
+ }
+
+ // 解析比赛项目的HTML结果页面
+ private Event parseEventResultsHtml(String eventName, String html) {
+ Event event = new Event(eventName);
+
+ // 解析决赛结果
+ List finalResults = parseResultsSection(html, "final");
+ event.setFinalResults(finalResults);
+
+ // 解析半决赛结果
+ List semifinalResults = parseResultsSection(html, "semifinal");
+ event.setSemifinalResults(semifinalResults);
+
+ // 解析初赛结果
+ List preliminaryResults = parseResultsSection(html, "preliminary");
+ event.setPreliminaryResults(preliminaryResults);
+
+ return event;
+ }
+
+ private List parseResultsSection(String html, String stage) {
+ List results = new ArrayList<>();
+ // 根据stage参数查找对应的HTML部分,然后解析结果
+ return results;
+ }
+
+ // 将所有选手信息输出到指定文件
+ public void displayAllPlayersInfo(String outputPath) {
+ try (FileWriter writer = new FileWriter(outputPath, true)){
+ // 创建副本,避免修改原始数据
+ List sortedPlayers = new ArrayList<>(allPlayers);
+
+ sortedPlayers.sort((p1, p2) -> {
+ // 1. 首先按国籍升序排序
+ int countryCompare = p1.getCountry().compareTo(p2.getCountry());
+ if (countryCompare != 0) {
+ return countryCompare;
+ }
+ // 2. 若国籍相同,则按姓氏升序排序
+ String[] nameParts1 = p1.getFullName().split(" ");
+ String[] nameParts2 = p2.getFullName().split(" ");
+ String lastName1 = nameParts1[nameParts1.length - 1];
+ String lastName2 = nameParts2[nameParts2.length - 1];
+ return lastName1.compareTo(lastName2);
+ });
+
+ // 遍历排序后的选手列表,按格式写入文件
+ for (Player player : sortedPlayers) {
+ writer.write("Full Name:" + player.getFullName() + "\n");
+ writer.write("Gender:" + player.getGender() + "\n");
+ writer.write("Country:" + player.getCountry() + "\n");
+ writer.write("-----\n");
+ }
+ }catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // 将指定比赛项目的决赛结果输出到文件
+ public boolean displayFinalResults(String eventName, String outputPath) {
+ Event event = allEvents.get(eventName);
+ if (event == null) {
+ return false;
+ }
+
+ List finalResults = event.getFinalResults();
+ if (finalResults == null || finalResults.isEmpty()) {
+ return false;
+ }
+ try (FileWriter writer = new FileWriter(outputPath, true)){
+ for (Result result : finalResults) {
+ writer.write("Full Name:" + result.getPlayerName() + "\n");
+ writer.write("Rank:" + result.getRank() + "\n");
+ writer.write("Score:" + result.getScoreString() + "\n");
+ writer.write("-----\n");
+ }
+ return true;
+ }catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ // 将指定比赛项目的详细结果(包括所有阶段)输出到文件
+ public boolean displayDetailedResults(String eventName, String outputPath) {
+ Event event = allEvents.get(eventName);
+ if (event == null) {
+ return false;
+ }
+
+ Map combinedResultsMap = new HashMap<>();
+
+ List preliminaryResults = event.getPreliminaryResults();
+ if (preliminaryResults != null) {
+ for (Result result : preliminaryResults) {
+ CombinedResult combined = new CombinedResult(result.getPlayerName());
+ combined.setPreliminaryResult(result);
+ combinedResultsMap.put(result.getPlayerName(), combined);
+ }
+ }
+
+ // 处理半决赛结果
+ List semifinalResults = event.getSemifinalResults();
+ if (semifinalResults != null) {
+ for (Result result : semifinalResults) {
+ // 使用getOrDefault确保即使没有初赛结果也能正确创建对象
+ CombinedResult combined = combinedResultsMap.getOrDefault(result.getPlayerName(),
+ new CombinedResult(result.getPlayerName()));
+ combined.setSemifinalResult(result);
+ combinedResultsMap.put(result.getPlayerName(), combined);
+ }
+ }
+
+ // 处理决赛结果
+ List finalResults = event.getFinalResults();
+ if (finalResults != null) {
+ for (Result result : finalResults) {
+ // 使用getOrDefault确保即使没有前面阶段的结果也能正确创建对象
+ CombinedResult combined = combinedResultsMap.getOrDefault(result.getPlayerName(),
+ new CombinedResult(result.getPlayerName()));
+ combined.setFinalResult(result);
+ combinedResultsMap.put(result.getPlayerName(), combined);
+ }
+ }
+
+ // 3. 转换为列表并按照第一次比赛的排名排序
+ List combinedResults = new ArrayList<>(combinedResultsMap.values());
+ Collections.sort(combinedResults, new Comparator() {
+ @Override
+ public int compare(CombinedResult c1, CombinedResult c2) {
+ // 【关键】按照第一次比赛的排名排序
+ // 优先级:初赛 -> 半决赛 -> 决赛
+ if (c1.getPreliminaryResult() != null && c2.getPreliminaryResult() != null) {
+ // 初赛排名比较
+ return Integer.parseInt(c1.getPreliminaryResult().getRank()) -
+ Integer.parseInt(c2.getPreliminaryResult().getRank());
+ } else if (c1.getSemifinalResult() != null && c2.getSemifinalResult() != null) {
+ // 半决赛排名比较
+ return Integer.parseInt(c1.getSemifinalResult().getRank()) -
+ Integer.parseInt(c2.getSemifinalResult().getRank());
+ } else if (c1.getFinalResult() != null && c2.getFinalResult() != null) {
+ // 决赛排名比较
+ return Integer.parseInt(c1.getFinalResult().getRank()) -
+ Integer.parseInt(c2.getFinalResult().getRank());
+ }
+ return 0; // 其他情况默认不排序
+ }
+ });
+
+ // 4. 输出详细结果到文件
+ try (FileWriter writer = new FileWriter(outputPath, true)) {
+ for (CombinedResult combined : combinedResults) {
+ writer.write("Full Name:" + combined.getPlayerName() + "\n");
+ writer.write("Rank:" + combined.getRankString() + "\n");
+ writer.write("Preliminary Score:" + combined.getPreliminaryScoreString() + "\n");
+ writer.write("Semifinal Score:" + combined.getSemifinalScoreString() + "\n");
+ writer.write("Final Score:" + combined.getFinalScoreString() + "\n");
+ writer.write("-----\n");
+ }
+ return true; // 输出成功
+ } catch (IOException e) {
+ // 异常处理:打印异常信息到控制台
+ e.printStackTrace();
+ return false; // 输出失败
+ }
+ }
+
+ // Player 类 —— 表示选手信息的内部数据模型
+ private static class Player {
+ // 封装选手的基本信息:姓名,性别,国籍
+ private String fullName;
+ private String gender;
+ private String country;
+
+ public Player(String fullName, String gender, String country) {
+ this.fullName = fullName;
+ this.gender = gender;
+ this.country = country;
+ }
+
+ public String getFullName() {
+ return fullName;
+ }
+
+ public String getGender() {
+ return gender;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+ }
+
+ // Event类 —— 比赛项目(包含多个阶段结果)
+ private static class Event {
+ private String name;
+ private List preliminaryResults;
+ private List semifinalResults;
+ private List finalResults;
+
+ public Event(String name) { this.name = name; }
+
+ public String getName() { return name; }
+
+ public List getPreliminaryResults() { return preliminaryResults; }
+
+ public List getSemifinalResults() { return semifinalResults; }
+
+ public List getFinalResults() { return finalResults; }
+
+ // 设置决赛结果
+ public void setFinalResults(List finalResults) { this.finalResults = finalResults; }
+
+ public void setPreliminaryResults(List preliminaryResults) { this.preliminaryResults = preliminaryResults; }
+
+ public void setSemifinalResults(List semifinalResults) { this.semifinalResults = semifinalResults; }
+ }
+
+ // Result类 —— 表示单阶段比赛结果
+ private static class Result {
+ // 封装选手在某个比赛阶段的成绩信息(包括排名,各伦分数,总分)
+ private String playerName;
+ private String rank;
+ private List scores;
+ private double totalScore;
+
+ public Result(String playerName, String rank, List scores, double totalScore) {
+ this.playerName = playerName;
+ this.rank = rank;
+ this.scores = scores;
+ this.totalScore = totalScore;
+ }
+
+ // Getter方法
+ public String getPlayerName() { return playerName; }
+
+ public String getRank() { return rank;}
+
+ public List getScores() { return scores; }
+
+ public double getTotalScore() { return totalScore; }
+
+ // 格式化分数字符串
+ public String getScoreString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < scores.size(); i++) {
+ if (i > 0) {
+ sb.append(" + "); // 为非第一个字符添加分隔符
+ }
+ sb.append(String.format("%.2f", scores.get(i)));
+ }
+ sb.append(" = ").append(String.format("%.2f", totalScore));
+ return sb.toString();
+ }
+ }
+
+ // CombineResult类 —— 所有比赛阶段综合结果
+ private static class CombinedResult {
+ private String playerName;
+ private Result preliminaryResult;
+ private Result semifinalResult;
+ private Result finalResult;
+
+ public CombinedResult(String name) { this.playerName = name; }
+
+ public String getPlayerName() { return playerName; }
+
+ public Result getPreliminaryResult() { return preliminaryResult; }
+
+ public void setPreliminaryResult(Result preliminaryResult) { this.preliminaryResult = preliminaryResult; }
+
+ public Result getSemifinalResult() { return semifinalResult; }
+
+ public void setSemifinalResult(Result semifinalResult) { this.semifinalResult = semifinalResult; }
+
+ public Result getFinalResult() { return finalResult; }
+
+ public void setFinalResult(Result finalResult) { this.finalResult = finalResult; }
+
+ public String getRankString() {
+ String preliminaryRank = preliminaryResult != null ? preliminaryResult.getRank() : "*";
+ String semifinalRank = semifinalResult != null ? semifinalResult.getRank() : "*";
+ String finalRank = finalResult != null ? finalResult.getRank() : "*";
+ return preliminaryRank + " | " + semifinalRank + " | " + finalRank;
+ }
+
+ // 获取初赛分数字符串,不存在的成绩用"*"表示
+ public String getPreliminaryScoreString() {
+ return preliminaryResult != null ? preliminaryResult.getScoreString() : "*";
+ }
+
+ // 获取半决赛分数字符串,不存在的成绩用"*"表示
+ public String getSemifinalScoreString() {
+ return semifinalResult != null ? semifinalResult.getScoreString() : "*";
+ }
+
+ // 获取决赛分数字符串,不存在的成绩用"*"表示
+ public String getFinalScoreString() {
+ return finalResult != null ? finalResult.getScoreString() : "*";
+ }
+
+ }
+}
diff --git a/work2/cicikh/cicikh/src/main/java/DWASearch.java b/work2/cicikh/cicikh/src/main/java/DWASearch.java
new file mode 100644
index 00000000..44fcfdc1
--- /dev/null
+++ b/work2/cicikh/cicikh/src/main/java/DWASearch.java
@@ -0,0 +1,165 @@
+import java.io.*;
+import java.util.Arrays;
+import java.util.List;
+
+public class DWASearch {
+ private static final List VALID_EVENTS = Arrays.asList(
+ "women 1m springboard", // 女子1米跳板
+ "women 3m springboard", // 女子3米跳板
+ "women 10m platform", // 女子10米跳台
+ "women 3m synchronised", // 女子3米双人跳板
+ "women 10m synchronised", // 女子10米双人跳台
+ "men 1m springboard", // 男子1米跳板
+ "men 3m springboard", // 男子3米跳板
+ "men 10m platform", // 男子10米跳台
+ "men 3m synchronised", // 男子3米双人跳板
+ "men 10m synchronised" // 男子10米双人跳台
+ );
+
+ public static void main(String[] args) {
+ if (args.length != 2) {
+ System.out.println("Usage: java DWASearch input.txt output.txt");
+ return;
+ }
+
+ String inputPath = args[0];
+ String outputPath = args[1];
+
+ // 验证输入文件
+ File inputFile = new File(inputPath);
+ if (!inputFile.exists()) {
+ System.err.println("错误:输入文件不存在: " + inputPath);
+ return;
+ }
+
+ // 确保输出目录存在
+ File outputFile = new File(outputPath);
+ File outputDir = outputFile.getParentFile();
+ if (outputDir != null && !outputDir.exists()) {
+ outputDir.mkdirs();
+ }
+
+ System.out.println("正在初始化核心模块并加载数据...");
+ CoreModule coreModule = new CoreModule();
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(inputFile));
+ FileWriter writer = new FileWriter(outputFile)) {
+ // FileWriter仅用于初始化文件,实际写入由各命令处理方法完成
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty()) {
+ continue;
+ }
+
+ processCommand(line, coreModule, outputPath);
+ }
+
+ System.out.println("处理完成!结果已保存到: " + outputPath);
+ } catch (IOException e) {
+ System.err.println("文件操作异常: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ private static void processCommand(String command, CoreModule coreModule, String outputPath) {
+ String[] parts = command.split("\\s+");
+
+ if (parts[0].equals("players")) {
+ // players命令:输出所有选手信息
+ if (parts.length != 1) {
+ // players命令不应该有额外参数
+ writeError(outputPath);
+ } else {
+ coreModule.displayAllPlayersInfo(outputPath);
+ }
+ } else if (parts[0].equals("result")) {
+ processResultCommand(parts, coreModule, outputPath);
+ } else {
+ writeError(outputPath);
+ }
+ }
+
+ private static void processResultCommand(String[] parts, CoreModule coreModule, String outputPath) {
+ // 检查命令格式:result命令至少需要包含比赛项目名称
+ if (parts.length < 2) {
+ writeError(outputPath);
+ return;
+ }
+
+ String eventName;
+ boolean hasDetail = false;
+
+ // 简化处理逻辑
+ if (parts.length == 2) {
+ // 格式:result eventName
+ eventName = parts[1];
+ } else if (parts.length == 3 && "detail".equals(parts[2])) {
+ // 格式:result eventName detail
+ eventName = parts[1];
+ hasDetail = true;
+ } else if (parts.length >= 3) {
+ // 处理带空格的比赛名称,如 "women 10m platform"
+ // 重构比赛名称:parts[1]到倒数第二个(或倒数第二个之前如果是detail)
+ StringBuilder eventNameBuilder = new StringBuilder();
+ int endIndex = parts.length;
+
+ // 检查最后一个是否是detail
+ if ("detail".equals(parts[parts.length - 1])) {
+ hasDetail = true;
+ endIndex = parts.length - 1;
+ }
+
+ // 拼接比赛名称
+ for (int i = 1; i < endIndex; i++) {
+ if (i > 1) {
+ eventNameBuilder.append(" ");
+ }
+ eventNameBuilder.append(parts[i]);
+ }
+ eventName = eventNameBuilder.toString();
+ } else {
+ writeError(outputPath);
+ return;
+ }
+
+ // 转换eventName格式:空格转连字符
+ if (eventName.contains(" ")) {
+ eventName = eventName.replaceAll("\\s+", "-").toLowerCase();
+ }
+
+ // 调用CoreModule执行查询操作
+ boolean success;
+ if (hasDetail) {
+ success = coreModule.displayDetailedResults(eventName, outputPath);
+ } else {
+ success = coreModule.displayFinalResults(eventName, outputPath);
+ }
+
+ // 处理查询失败情况
+ if (!success) {
+ writeNA(outputPath);
+ }
+ }
+
+ private static void writeError(String outputPath) {
+ try (FileWriter writer = new FileWriter(outputPath, true)) {
+ writer.write("Error\n");
+ writer.write("-----\n");
+ } catch (IOException e) {
+ System.err.println("写入错误信息失败:" + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ private static void writeNA(String outputPath) {
+ try (FileWriter writer = new FileWriter(outputPath, true)) {
+ writer.write("N/A\n");
+ writer.write("-----\n");
+ } catch (IOException e) {
+ System.err.println("写入N/A信息失败:" + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/src/main/java/Lib.java b/work2/cicikh/cicikh/src/main/java/Lib.java
new file mode 100644
index 00000000..59ad4efc
--- /dev/null
+++ b/work2/cicikh/cicikh/src/main/java/Lib.java
@@ -0,0 +1,161 @@
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.*;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Lib {
+ // 发送HTTP GET请求并获取相应内容
+ public static String sendHttpRequest(String url) throws IOException {
+ CloseableHttpClient httpClient = HttpClients.createDefault();
+ HttpGet httpGet = new HttpGet(url);
+
+ // User-Agent: 标识客户端类型和版本
+ httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
+ // Accept: 声明客户端可接受的响应内容类型
+ httpGet.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8");
+ // Accept-Language: 声明客户端可接受的语言
+ httpGet.addHeader("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
+ // Referer: 标识请求的来源页面
+ httpGet.addHeader("Referer", url);
+
+ try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ throw new IOException("Http request failed with status code: " + statusCode);
+ }
+ return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+ } finally {
+ httpClient.close();
+ }
+ }
+
+ // 根据标签名和属性从HTML中提取特定内容
+ public static String extractContentFromHtml(String html, String tag, String attribute, String value) {
+ String regex = "<" + tag + "[^>]*" + attribute + "=[\\\"']" + value + "[\\\"'][^>]*>(.*?)" + tag + ">";
+ Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
+ Matcher matcher = pattern.matcher(html);
+
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ return null;
+ }
+
+ // 从HTML中提取器所有匹配正则表达式的内容
+ public static List extractAllMatches(String html, String regex) {
+ List matches = new ArrayList<>();
+ Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
+ Matcher matcher = pattern.matcher(html);
+
+ while (matcher.find()) {
+ matches.add(matcher.group(1));
+ }
+ return matches;
+ }
+
+ // 清理HtML内容,移除所有HTML标签并规范化文本格式
+ public static String cleanHtmlTags(String html) {
+ if (html == null) {
+ return null;
+ }
+
+ // 移除所有HTML标签:<[^>]*> 匹配<开头,>结尾的所有内容
+ String text = html.replaceAll("<[^>]*>", "");
+ // 移除多余的空格和换行符:\s+ 匹配一个或多个空白字符
+ text = text.replaceAll("\\s+", " ").trim();
+ return text;
+ }
+
+ // 将字符串内容保存到指定文件
+ public static void saveToFile(String content, String filePath) throws IOException {
+ try (Writer writer = new OutputStreamWriter(new FileOutputStream(filePath), StandardCharsets.UTF_8)){
+ writer.write(content);
+ }
+ }
+
+ // 从指定文件读取内容
+ public static String readFromFile(String filePath) throws IOException {
+ StringBuilder content = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8))){
+ String line;
+ // 逐行读取文件内容
+ while ((line = reader.readLine()) != null) {
+ content.append(line).append("\n");
+ }
+ }
+ return content.toString();
+ }
+
+ // 对URL编码的字符串进行编码
+ public static String urlDecode(String encodedString) {
+ try {
+ return URLDecoder.decode(encodedString, "UTF-8");
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // 将分数列表格式化为易读的字符串
+ public static String formatScoreString(List scores, double totalScore) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < scores.size(); i++) {
+ if (i > 0) {
+ sb.append(" + "); // 为非第一个分数添加分隔符
+ }
+ // 保留两位小数,提高可读性
+ sb.append(String.format("%.2f", scores.get(i)));
+ }
+ sb.append(" = ").append(String.format("%.2f", totalScore));
+ return sb.toString();
+ }
+
+ // 将分数字符串解析为Double类型的列表
+ public static List parseScores(String scoreString) {
+ List scores = new ArrayList<>();
+ String[] parts = scoreString.split("\\s+");
+ for (String part : parts) {
+ try {
+ scores.add(Double.parseDouble(part));
+ } catch (NumberFormatException e) {
+ // 忽略无法解析的部分
+ }
+ }
+ return scores;
+ }
+
+ // 计算分数列表的总分
+ public static double calculateTotalScore(List scores) {
+ double total = 0.0;
+ for (Double score : scores) {
+ total += score;
+ }
+ return total;
+ }
+
+ // 验证比赛项目名称是否合法
+ public static boolean isValidEventName(String eventName) {
+ List validEvents = Arrays.asList(
+ "women 1m springboard", // 女子1米跳板
+ "women 3m springboard", // 女子3米跳板
+ "women 10m platform", // 女子10米跳台
+ "women 3m synchronised", // 女子双人3米跳板
+ "women 10m synchronised", // 女子双人10米跳台
+ "men 1m springboard", // 男子1米跳板
+ "men 3m springboard", // 男子3米跳板
+ "men 10m platform", // 男子10米跳台
+ "men 3m synchronised", // 男子双人3米跳板
+ "men 10m synchronised" // 男子双人10米跳台
+ );
+ return validEvents.contains(eventName);
+ }
+}
diff --git a/work2/cicikh/cicikh/src/main/java/input.txt b/work2/cicikh/cicikh/src/main/java/input.txt
new file mode 100644
index 00000000..fd4e5dda
--- /dev/null
+++ b/work2/cicikh/cicikh/src/main/java/input.txt
@@ -0,0 +1,3 @@
+players
+result women-10m
+result women-10m detail
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/src/test/java/CoreModuleTest.java b/work2/cicikh/cicikh/src/test/java/CoreModuleTest.java
new file mode 100644
index 00000000..575b0f14
--- /dev/null
+++ b/work2/cicikh/cicikh/src/test/java/CoreModuleTest.java
@@ -0,0 +1,359 @@
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * CoreModule的单元测试类
+ */
+public class CoreModuleTest {
+ private static final String TEST_OUTPUT_FILE = "test_output.txt";
+
+ /**
+ * 测试CoreModule初始化
+ */
+ @Test
+ public void testCoreModuleInitialization() {
+ CoreModule coreModule = new CoreModule();
+ assertNotNull(coreModule);
+ }
+
+ /**
+ * 测试输出所有选手信息
+ */
+ @Test
+ public void testDisplayAllPlayersInfo() {
+ // 创建CoreModule实例
+ CoreModule coreModule = new CoreModule();
+
+ // 删除之前的测试文件
+ deleteTestFile();
+
+ // 调用方法
+ coreModule.displayAllPlayersInfo(TEST_OUTPUT_FILE);
+
+ // 验证文件存在且有内容
+ File file = new File(TEST_OUTPUT_FILE);
+ assertTrue(file.exists());
+ assertTrue(file.length() > 0);
+
+ // 验证文件内容格式
+ try (BufferedReader reader = new BufferedReader(new FileReader(TEST_OUTPUT_FILE))) {
+ String line;
+ int playerCount = 0;
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("Full Name:")) {
+ playerCount++;
+ // 验证性别行
+ line = reader.readLine();
+ assertNotNull(line);
+ assertTrue(line.startsWith("Gender:"));
+ // 验证国籍行
+ line = reader.readLine();
+ assertNotNull(line);
+ assertTrue(line.startsWith("Country:"));
+ // 验证分隔线
+ line = reader.readLine();
+ assertNotNull(line);
+ assertEquals("-----", line);
+ }
+ }
+ // 至少有一个选手
+ assertTrue(playerCount > 0);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail("读取测试文件失败");
+ }
+ }
+
+ /**
+ * 测试输出决赛结果(有效比赛项目)
+ */
+ @Test
+ public void testDisplayFinalResultsValidEvent() {
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ // 使用一个已知存在的比赛项目(来自模拟数据)
+ boolean success = coreModule.displayFinalResults("women-10m", TEST_OUTPUT_FILE);
+
+ // 验证是否成功
+ assertTrue(success);
+
+ // 验证文件内容
+ File file = new File(TEST_OUTPUT_FILE);
+ assertTrue(file.exists());
+ assertTrue(file.length() > 0);
+ }
+
+ /**
+ * 测试输出决赛结果(无效比赛项目)
+ */
+ @Test
+ public void testDisplayFinalResultsInvalidEvent() {
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ // 使用一个不存在的比赛项目
+ boolean success = coreModule.displayFinalResults("invalid event", TEST_OUTPUT_FILE);
+
+ // 验证是否失败
+ assertFalse(success);
+ }
+
+ /**
+ * 测试输出详细结果(有效比赛项目)
+ */
+ @Test
+ public void testDisplayDetailedResultsValidEvent() {
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ // 使用一个已知存在的比赛项目(来自模拟数据)
+ boolean success = coreModule.displayDetailedResults("women-10m", TEST_OUTPUT_FILE);
+
+ // 验证是否成功
+ assertTrue(success);
+
+ // 验证文件内容
+ File file = new File(TEST_OUTPUT_FILE);
+ assertTrue(file.exists());
+ assertTrue(file.length() > 0);
+ }
+
+ /**
+ * 测试输出详细结果(无效比赛项目)
+ */
+ @Test
+ public void testDisplayDetailedResultsInvalidEvent() {
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ // 使用一个不存在的比赛项目
+ boolean success = coreModule.displayDetailedResults("invalid event", TEST_OUTPUT_FILE);
+
+ // 验证是否失败
+ assertFalse(success);
+ }
+
+ /**
+ * 测试选手信息排序
+ */
+ @Test
+ public void testPlayerSorting() {
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ coreModule.displayAllPlayersInfo(TEST_OUTPUT_FILE);
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(TEST_OUTPUT_FILE))) {
+ String line;
+ String previousCountry = "";
+ String previousLastName = "";
+ boolean firstPlayer = true;
+
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("Full Name:")) {
+ // 解析选手信息
+ String fullName = line.substring(11);
+ line = reader.readLine(); // Gender
+ line = reader.readLine(); // Country
+ String country = line.substring(8).trim();
+
+ // 解析姓氏
+ String[] nameParts = fullName.split(" ");
+ String lastName = nameParts[nameParts.length - 1];
+
+ if (!firstPlayer) {
+ // 验证排序:国籍升序,姓氏升序
+ if (country.equals(previousCountry)) {
+ assertTrue(lastName.compareTo(previousLastName) >= 0);
+ } else {
+ assertTrue(country.compareTo(previousCountry) > 0);
+ }
+ }
+
+ previousCountry = country;
+ previousLastName = lastName;
+ firstPlayer = false;
+
+ // 跳过分隔线
+ reader.readLine();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail("读取测试文件失败");
+ }
+ }
+
+ /**
+ * 测试文件追加功能
+ */
+ @Test
+ public void testFileAppend() {
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ // 第一次调用
+ coreModule.displayAllPlayersInfo(TEST_OUTPUT_FILE);
+ long length1 = new File(TEST_OUTPUT_FILE).length();
+
+ // 第二次调用
+ coreModule.displayAllPlayersInfo(TEST_OUTPUT_FILE);
+ long length2 = new File(TEST_OUTPUT_FILE).length();
+
+ // 验证文件长度增加
+ assertTrue(length2 > length1);
+ }
+
+ /**
+ * 测试输出格式
+ */
+ @Test
+ public void testOutputFormat() {
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ coreModule.displayFinalResults("women-10m", TEST_OUTPUT_FILE);
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(TEST_OUTPUT_FILE))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("Full Name:")) {
+ // 验证格式
+ assertNotNull(line.substring(11));
+
+ line = reader.readLine(); // Rank
+ assertNotNull(line);
+ assertTrue(line.startsWith("Rank:"));
+ assertNotNull(line.substring(5));
+
+ line = reader.readLine(); // Score
+ assertNotNull(line);
+ assertTrue(line.startsWith("Score:"));
+ assertNotNull(line.substring(6));
+
+ line = reader.readLine(); // Separator
+ assertNotNull(line);
+ assertEquals("-----", line);
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail("读取测试文件失败");
+ }
+ }
+
+ /**
+ * 测试比赛结果排序和分数计算
+ * 验证排名是否正确,分数格式是否正确
+ */
+ @Test
+ public void testResultsRankingAndScores() {
+ System.out.println("=== 测试比赛结果排序和分数计算 ===");
+
+ CoreModule coreModule = new CoreModule();
+ deleteTestFile();
+
+ // 测试女子10米跳台决赛结果
+ boolean success = coreModule.displayFinalResults("women-10m", TEST_OUTPUT_FILE);
+ assertTrue("有效的比赛项目应该输出成功", success);
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(TEST_OUTPUT_FILE))) {
+ String line;
+ int previousRank = 0;
+ double previousTotalScore = 0.0;
+ boolean firstResult = true;
+ int resultCount = 0;
+
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("Full Name:")) {
+ resultCount++;
+ String playerName = line.substring(11);
+ System.out.println("选手 " + resultCount + ": " + playerName);
+
+ // 读取Rank行
+ line = reader.readLine();
+ assertNotNull("Rank行不能为空", line);
+ assertTrue("Rank行应该以'Rank:'开头: " + line, line.startsWith("Rank:"));
+
+ String rankStr = line.substring(5).trim();
+ int currentRank = Integer.parseInt(rankStr);
+ System.out.println(" 排名: " + currentRank);
+
+ // 读取Score行
+ line = reader.readLine();
+ assertNotNull("Score行不能为空", line);
+ assertTrue("Score行应该以'Score:'开头: " + line, line.startsWith("Score:"));
+
+ String scoreStr = line.substring(6).trim();
+ System.out.println(" 分数: " + scoreStr);
+
+ // 验证分数格式:应该包含 " + " 和 " = "
+ assertTrue("分数格式应该包含' + ': " + scoreStr,
+ scoreStr.contains(" + "));
+ assertTrue("分数格式应该包含' = ': " + scoreStr,
+ scoreStr.contains(" = "));
+
+ // 解析总分
+ String[] scoreParts = scoreStr.split(" = ");
+ assertEquals("分数应该被等号分成两部分: " + scoreStr, 2, scoreParts.length);
+
+ double totalScore = Double.parseDouble(scoreParts[1]);
+ System.out.println(" 总分: " + totalScore);
+
+ if (!firstResult) {
+ // 验证排名顺序:应该是1, 2, 3... 递增
+ assertTrue("排名应该递增: " + previousRank + " < " + currentRank,
+ currentRank > previousRank);
+
+ if (totalScore > previousTotalScore) {
+ System.out.println(" 注意:排名" + currentRank + "的分数(" + totalScore +
+ ") > 排名" + previousRank + "的分数(" + previousTotalScore + ")");
+ }
+ }
+
+ previousRank = currentRank;
+ previousTotalScore = totalScore;
+ firstResult = false;
+
+ // 验证分隔线
+ line = reader.readLine();
+ assertNotNull("分隔线不能为空", line);
+ assertEquals("分隔线应该是'-----'", "-----", line);
+
+ System.out.println();
+ }
+ }
+
+ // 验证至少有一个结果
+ assertTrue("应该至少有一个比赛结果", resultCount > 0);
+ System.out.println("总计: " + resultCount + " 个比赛结果");
+
+ // 女子10米跳台应该有5个结果(来自模拟数据)
+ assertEquals("女子10米跳台应该有5个决赛结果", 5, resultCount);
+
+ } catch (IOException e) {
+ fail("读取测试文件失败: " + e.getMessage());
+ } catch (NumberFormatException e) {
+ fail("数据格式错误(无法解析排名或分数): " + e.getMessage());
+ }
+
+ System.out.println("=== 测试完成 ===\n");
+ }
+
+ /**
+ * 辅助方法:删除测试文件
+ */
+ private void deleteTestFile() {
+ File file = new File(TEST_OUTPUT_FILE);
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+}
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/target/DWASearch-1.0-SNAPSHOT-jar-with-dependencies.jar b/work2/cicikh/cicikh/target/DWASearch-1.0-SNAPSHOT-jar-with-dependencies.jar
new file mode 100644
index 00000000..e985ca94
Binary files /dev/null and b/work2/cicikh/cicikh/target/DWASearch-1.0-SNAPSHOT-jar-with-dependencies.jar differ
diff --git a/work2/cicikh/cicikh/target/DWASearch-1.0-SNAPSHOT.jar b/work2/cicikh/cicikh/target/DWASearch-1.0-SNAPSHOT.jar
new file mode 100644
index 00000000..f13b81a7
Binary files /dev/null and b/work2/cicikh/cicikh/target/DWASearch-1.0-SNAPSHOT.jar differ
diff --git a/work2/cicikh/cicikh/target/classes/CoreModule$1.class b/work2/cicikh/cicikh/target/classes/CoreModule$1.class
new file mode 100644
index 00000000..71b42db6
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/CoreModule$1.class differ
diff --git a/work2/cicikh/cicikh/target/classes/CoreModule$2.class b/work2/cicikh/cicikh/target/classes/CoreModule$2.class
new file mode 100644
index 00000000..647d534b
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/CoreModule$2.class differ
diff --git a/work2/cicikh/cicikh/target/classes/CoreModule$CombinedResult.class b/work2/cicikh/cicikh/target/classes/CoreModule$CombinedResult.class
new file mode 100644
index 00000000..5ce2b48f
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/CoreModule$CombinedResult.class differ
diff --git a/work2/cicikh/cicikh/target/classes/CoreModule$Event.class b/work2/cicikh/cicikh/target/classes/CoreModule$Event.class
new file mode 100644
index 00000000..7288ff94
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/CoreModule$Event.class differ
diff --git a/work2/cicikh/cicikh/target/classes/CoreModule$Player.class b/work2/cicikh/cicikh/target/classes/CoreModule$Player.class
new file mode 100644
index 00000000..8fa7173e
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/CoreModule$Player.class differ
diff --git a/work2/cicikh/cicikh/target/classes/CoreModule$Result.class b/work2/cicikh/cicikh/target/classes/CoreModule$Result.class
new file mode 100644
index 00000000..66352d8f
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/CoreModule$Result.class differ
diff --git a/work2/cicikh/cicikh/target/classes/CoreModule.class b/work2/cicikh/cicikh/target/classes/CoreModule.class
new file mode 100644
index 00000000..55d44181
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/CoreModule.class differ
diff --git a/work2/cicikh/cicikh/target/classes/DWASearch.class b/work2/cicikh/cicikh/target/classes/DWASearch.class
new file mode 100644
index 00000000..b347d587
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/DWASearch.class differ
diff --git a/work2/cicikh/cicikh/target/classes/Lib.class b/work2/cicikh/cicikh/target/classes/Lib.class
new file mode 100644
index 00000000..d4cde85b
Binary files /dev/null and b/work2/cicikh/cicikh/target/classes/Lib.class differ
diff --git a/work2/cicikh/cicikh/target/maven-archiver/pom.properties b/work2/cicikh/cicikh/target/maven-archiver/pom.properties
new file mode 100644
index 00000000..52e6e7c3
--- /dev/null
+++ b/work2/cicikh/cicikh/target/maven-archiver/pom.properties
@@ -0,0 +1,3 @@
+artifactId=DWASearch
+groupId=com.fyxkh
+version=1.0-SNAPSHOT
diff --git a/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 00000000..46373ce1
--- /dev/null
+++ b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,9 @@
+CoreModule$Event.class
+CoreModule$1.class
+CoreModule$CombinedResult.class
+CoreModule.class
+CoreModule$Player.class
+Lib.class
+DWASearch.class
+CoreModule$Result.class
+CoreModule$2.class
diff --git a/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 00000000..dd37ff24
--- /dev/null
+++ b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,3 @@
+D:\west2-online\第二轮考核\cicikh\cicikh\src\main\java\CoreModule.java
+D:\west2-online\第二轮考核\cicikh\cicikh\src\main\java\DWASearch.java
+D:\west2-online\第二轮考核\cicikh\cicikh\src\main\java\Lib.java
diff --git a/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
new file mode 100644
index 00000000..0e70484a
--- /dev/null
+++ b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
@@ -0,0 +1 @@
+CoreModuleTest.class
diff --git a/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
new file mode 100644
index 00000000..a70f5947
--- /dev/null
+++ b/work2/cicikh/cicikh/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
@@ -0,0 +1 @@
+D:\west2-online\第二轮考核\cicikh\cicikh\src\test\java\CoreModuleTest.java
diff --git a/work2/cicikh/cicikh/target/surefire-reports/2025-12-07T13-33-11_073.dumpstream b/work2/cicikh/cicikh/target/surefire-reports/2025-12-07T13-33-11_073.dumpstream
new file mode 100644
index 00000000..6b92b253
--- /dev/null
+++ b/work2/cicikh/cicikh/target/surefire-reports/2025-12-07T13-33-11_073.dumpstream
@@ -0,0 +1,5 @@
+# Created at 2025-12-07T13:33:13.654
+Boot Manifest-JAR contains absolute paths in classpath 'D:\Apache Maven 3.9.10\apache-maven-3.9.10\conf\repo\org\apache\maven\surefire\surefire-booter\3.2.5\surefire-booter-3.2.5.jar'
+Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true
+'other' has different root
+
diff --git a/work2/cicikh/cicikh/target/surefire-reports/CoreModuleTest.txt b/work2/cicikh/cicikh/target/surefire-reports/CoreModuleTest.txt
new file mode 100644
index 00000000..3eeeebec
--- /dev/null
+++ b/work2/cicikh/cicikh/target/surefire-reports/CoreModuleTest.txt
@@ -0,0 +1,4 @@
+-------------------------------------------------------------------------------
+Test set: CoreModuleTest
+-------------------------------------------------------------------------------
+Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.105 s -- in CoreModuleTest
diff --git a/work2/cicikh/cicikh/target/surefire-reports/TEST-CoreModuleTest.xml b/work2/cicikh/cicikh/target/surefire-reports/TEST-CoreModuleTest.xml
new file mode 100644
index 00000000..26463b31
--- /dev/null
+++ b/work2/cicikh/cicikh/target/surefire-reports/TEST-CoreModuleTest.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work2/cicikh/cicikh/target/test-classes/CoreModuleTest.class b/work2/cicikh/cicikh/target/test-classes/CoreModuleTest.class
new file mode 100644
index 00000000..9c46240f
Binary files /dev/null and b/work2/cicikh/cicikh/target/test-classes/CoreModuleTest.class differ
diff --git a/work2/cicikh/cicikh/test_output.txt b/work2/cicikh/cicikh/test_output.txt
new file mode 100644
index 00000000..5257b9bd
--- /dev/null
+++ b/work2/cicikh/cicikh/test_output.txt
@@ -0,0 +1,20 @@
+Full Name:MULLER Jette
+Rank:1
+Score:85.00 + 82.00 + 88.00 + 86.00 + 89.00 = 430.00
+-----
+Full Name:ROLLINSON Amy
+Rank:2
+Score:83.00 + 80.00 + 85.00 + 82.00 + 86.00 = 416.00
+-----
+Full Name:SANTIAGO Dominique
+Rank:3
+Score:80.00 + 78.00 + 82.00 + 81.00 + 83.00 = 404.00
+-----
+Full Name:FUNG Katelyn
+Rank:4
+Score:78.00 + 75.00 + 80.00 + 79.00 + 81.00 = 393.00
+-----
+Full Name:DICK Elaena
+Rank:5
+Score:76.00 + 73.00 + 78.00 + 77.00 + 79.00 = 383.00
+-----