Scala 文件读写与成绩分析
1. 基础文件读取与成绩解析
1.1 读取成绩文件并简单显示
package ex_score
import scala.io.Source
import scala.util.{Try, Using}
object ScoreAnalyzerBasic {
def main(args: Array[String]): Unit = {
// 方法1:基本读取
readAndDisplayScores("score.txt")
// 方法2:使用资源管理
readScoresSafely("score.txt") match {
case scala.util.Success(scores) =>
println(s"成功读取 ${scores.length} 条成绩记录")
displayScores(scores)
case scala.util.Failure(exception) =>
println(s"读取失败: ${exception.getMessage}")
}
}
// 基本读取方法
def readAndDisplayScores(filePath: String): Unit = {
println("=== 学生成绩单 ===")
val source = Source.fromFile(filePath, "UTF-8")
try {
val lines = source.getLines().toList
// 打印标题行
if (lines.nonEmpty) {
println("标题: " + lines.head)
println("-" * 40)
}
// 打印学生成绩
lines.tail.foreach(println)
} finally {
source.close()
}
}
// 安全的读取方法
def readScoresSafely(filePath: String): Try[List[String]] = {
Using(Source.fromFile(filePath, "UTF-8")) { source =>
source.getLines().toList
}
}
def displayScores(lines: List[String]): Unit = {
if (lines.nonEmpty) {
println("成绩数据:")
lines.zipWithIndex.foreach { case (line, index) =>
println(s"${index + 1}. $line")
}
}
}
}
2. 完整成绩分析系统
2.1 定义数据模型
package ex_score
// 学生成绩数据类
case class StudentScore(
name: String,
chinese: Int,
math: Int,
english: Int
) {
// 计算总分
def totalScore: Int = chinese + math + english
// 计算平均分
def averageScore: Double = totalScore / 3.0
// 计算单科等级
def getGrade(score: Int): String = score match {
case s if s >= 90 => "A"
case s if s >= 80 => "B"
case s if s >= 70 => "C"
case s if s >= 60 => "D"
case _ => "F"
}
def chineseGrade: String = getGrade(chinese)
def mathGrade: String = getGrade(math)
def englishGrade: String = getGrade(english)
// 总体等级
def overallGrade: String = getGrade(averageScore.toInt)
override def toString: String = {
f"$name%-8s 语文:$chinese%3d($chineseGrade) 数学:$math%3d($mathGrade) 英语:$english%3d($englishGrade) " +
f"总分:$totalScore%3d 平均分:${averageScore}%.1f 总评:$overallGrade"
}
}
// 班级统计信息
case class ClassStatistics(
subjectAverages: Map[String, Double],
topStudents: List[StudentScore],
gradeDistribution: Map[String, Int],
totalStudents: Int
)
2.2 成绩解析器
package ex_score
import scala.io.Source
import scala.util.{Try, Success, Failure}
object ScoreParser {
// 解析一行成绩数据
def parseScoreLine(line: String): Option[StudentScore] = {
try {
// 分割字符串:姓名:语文,数学,英语
val parts = line.split(":") // 注意:使用中文冒号
if (parts.length != 2) return None
val name = parts(0).trim
val scoreStr = parts(1).trim
// 分割成绩:87,92,88
val scores = scoreStr.split(",") // 注意:使用中文逗号
if (scores.length != 3) return None
Some(StudentScore(
name = name,
chinese = scores(0).trim.toInt,
math = scores(1).trim.toInt,
english = scores(2).trim.toInt
))
} catch {
case e: Exception =>
println(s"解析行失败 '$line': ${e.getMessage}")
None
}
}
// 读取并解析整个文件
def readAndParseScores(filePath: String): Try[List[StudentScore]] = {
Try {
val source = Source.fromFile(filePath, "UTF-8")
try {
val lines = source.getLines().toList
if (lines.isEmpty) {
throw new IllegalArgumentException("文件为空")
}
// 跳过标题行,解析学生成绩
val studentScores = lines.tail
.filter(_.trim.nonEmpty) // 过滤空行
.flatMap(parseScoreLine) // 解析每行
if (studentScores.isEmpty) {
throw new IllegalArgumentException("未找到有效的成绩数据")
}
studentScores
} finally {
source.close()
}
}
}
// 验证成绩数据
def validateScores(scores: List[StudentScore]): List[String] = {
scores.flatMap { score =>
var errors = List[String]()
// 检查成绩范围
def validateSubject(subject: String, scoreValue: Int): Unit = {
if (scoreValue < 0 || scoreValue > 100) {
errors :+= s"${score.name}的${subject}成绩异常: $scoreValue"
}
}
validateSubject("语文", score.chinese)
validateSubject("数学", score.math)
validateSubject("英语", score.english)
errors
}
}
}
2.3 成绩统计分析
package ex_score
import scala.math.Ordering
object ScoreAnalyzer {
// 1. 计算单科平均分
def calculateSubjectAverages(scores: List[StudentScore]): Map[String, Double] = {
if (scores.isEmpty) return Map.empty
val chineseAvg = scores.map(_.chinese).sum.toDouble / scores.length
val mathAvg = scores.map(_.math).sum.toDouble / scores.length
val englishAvg = scores.map(_.english).sum.toDouble / scores.length
Map(
"语文" -> chineseAvg,
"数学" -> mathAvg,
"英语" -> englishAvg
)
}
// 2. 按总分排名
def rankByTotalScore(scores: List[StudentScore]): List[StudentScore] = {
scores.sortBy(s => (-s.totalScore, s.name))
}
// 3. 按单科排名
def rankBySubject(scores: List[StudentScore], subject: String): List[(String, Int, Int)] = {
val ranked = subject.toLowerCase match {
case "语文" => scores.sortBy(s => (-s.chinese, s.name))
case "数学" => scores.sortBy(s => (-s.math, s.name))
case "英语" => scores.sortBy(s => (-s.english, s.name))
case _ => scores
}
ranked.zipWithIndex.map { case (score, rank) =>
val subjectScore = subject.toLowerCase match {
case "语文" => score.chinese
case "数学" => score.math
case "英语" => score.english
case _ => 0
}
(score.name, subjectScore, rank + 1)
}
}
// 4. 计算各分数段人数
def calculateScoreRanges(scores: List[StudentScore]): Map[String, Map[String, Int]] = {
def countByGrade(subjectScores: List[Int]): Map[String, Int] = {
val grades = subjectScores.map { score =>
score match {
case s if s >= 90 => "优秀(90-100)"
case s if s >= 80 => "良好(80-89)"
case s if s >= 70 => "中等(70-79)"
case s if s >= 60 => "及格(60-69)"
case _ => "不及格(0-59)"
}
}
grades.groupBy(identity).view.mapValues(_.size).toMap
}
Map(
"语文" -> countByGrade(scores.map(_.chinese)),
"数学" -> countByGrade(scores.map(_.math)),
"英语" -> countByGrade(scores.map(_.english))
)
}
// 5. 查找优秀学生(各科都>=85)
def findExcellentStudents(scores: List[StudentScore]): List[StudentScore] = {
scores.filter { s =>
s.chinese >= 85 && s.math >= 85 && s.english >= 85
}
}
// 6. 查找需要关注的学生(有不及格科目)
def findStudentsNeedingAttention(scores: List[StudentScore]): List[(StudentScore, List[String])] = {
scores.flatMap { s =>
val failingSubjects = List(
if (s.chinese < 60) Some("语文") else None,
if (s.math < 60) Some("数学") else None,
if (s.english < 60) Some("英语") else None
).flatten
if (failingSubjects.nonEmpty) {
Some((s, failingSubjects))
} else {
None
}
}
}
// 7. 计算班级整体统计
def calculateClassStatistics(scores: List[StudentScore]): ClassStatistics = {
val subjectAverages = calculateSubjectAverages(scores)
val topStudents = rankByTotalScore(scores).take(3)
// 计算等级分布
val grades = scores.map(_.overallGrade)
val gradeDistribution = grades.groupBy(identity).view.mapValues(_.size).toMap
ClassStatistics(
subjectAverages = subjectAverages,
topStudents = topStudents,
gradeDistribution = gradeDistribution,
totalStudents = scores.length
)
}
}
2.4 成绩报告生成
package ex_score
import java.io.{PrintWriter, File}
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
object ScoreReportGenerator {
// 生成文本报告
def generateTextReport(
scores: List[StudentScore],
outputPath: String = "成绩分析报告.txt"
): Unit = {
val writer = new PrintWriter(new File(outputPath), "UTF-8")
try {
// 报告头部
writer.println("=" * 60)
writer.println(" 学生成绩分析报告")
writer.println("=" * 60)
writer.println(s"生成时间: ${LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))}")
writer.println(s"分析人数: ${scores.length} 人")
writer.println()
// 1. 学生成绩详情
writer.println("一、学生成绩详情")
writer.println("-" * 60)
writer.println(f"${"姓名"}%-8s ${"语文"}%6s ${"数学"}%6s ${"英语"}%6s ${"总分"}%6s ${"平均分"}%8s ${"等级"}%4s")
writer.println("-" * 60)
val rankedScores = ScoreAnalyzer.rankByTotalScore(scores)
rankedScores.foreach { score =>
writer.println(f"${score.name}%-8s ${score.chinese}%6d ${score.math}%6d ${score.english}%6d " +
f"${score.totalScore}%6d ${score.averageScore}%8.1f ${score.overallGrade}%4s")
}
writer.println()
// 2. 科目平均分
writer.println("二、各科目平均分")
writer.println("-" * 60)
val subjectAverages = ScoreAnalyzer.calculateSubjectAverages(scores)
subjectAverages.foreach { case (subject, avg) =>
writer.println(f"$subject%4s: ${avg}%.2f 分")
}
writer.println()
// 3. 分数段统计
writer.println("三、各科目分数段统计")
writer.println("-" * 60)
val scoreRanges = ScoreAnalyzer.calculateScoreRanges(scores)
scoreRanges.foreach { case (subject, ranges) =>
writer.println(s"$subject:")
ranges.toList.sortBy(_._1).foreach { case (range, count) =>
writer.println(f" $range%-12s: $count%2d 人")
}
}
writer.println()
// 4. 优秀学生
writer.println("四、优秀学生(各科≥85分)")
writer.println("-" * 60)
val excellentStudents = ScoreAnalyzer.findExcellentStudents(scores)
if (excellentStudents.isEmpty) {
writer.println("暂无")
} else {
excellentStudents.foreach { s =>
writer.println(s"${s.name}:语文${s.chinese} 数学${s.math} 英语${s.english} 总分${s.totalScore}")
}
}
writer.println()
// 5. 需要关注的学生
writer.println("五、需要关注的学生(有不及格科目)")
writer.println("-" * 60)
val studentsNeedingAttention = ScoreAnalyzer.findStudentsNeedingAttention(scores)
if (studentsNeedingAttention.isEmpty) {
writer.println("无")
} else {
studentsNeedingAttention.foreach { case (student, subjects) =>
writer.println(s"${student.name}:不及格科目:${subjects.mkString("、")}")
}
}
writer.println()
// 6. 前三名学生
writer.println("六、总分前三名学生")
writer.println("-" * 60)
val topThree = rankedScores.take(3)
topThree.zipWithIndex.foreach { case (student, index) =>
writer.println(f"第${index + 1}名:${student.name}%-8s 总分:${student.totalScore}%3d")
}
// 报告尾部
writer.println()
writer.println("=" * 60)
writer.println("报告结束")
} finally {
writer.close()
}
}
// 生成HTML报告
def generateHtmlReport(
scores: List[StudentScore],
outputPath: String = "成绩分析报告.html"
): Unit = {
val writer = new PrintWriter(new File(outputPath), "UTF-8")
try {
val rankedScores = ScoreAnalyzer.rankByTotalScore(scores)
val subjectAverages = ScoreAnalyzer.calculateSubjectAverages(scores)
val topThree = rankedScores.take(3)
writer.println("""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>学生成绩分析报告</title>
<style>
body { font-family: 'Microsoft YaHei', Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.1); }
h1 { color: #2c3e50; text-align: center; border-bottom: 3px solid #3498db; padding-bottom: 15px; }
h2 { color: #3498db; border-left: 5px solid #3498db; padding-left: 15px; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th { background-color: #3498db; color: white; padding: 12px; text-align: center; }
td { padding: 10px; border: 1px solid #ddd; text-align: center; }
tr:nth-child(even) { background-color: #f9f9f9; }
tr:hover { background-color: #f1f1f1; }
.excellent { background-color: #d4edda !important; }
.warning { background-color: #fff3cd !important; }
.danger { background-color: #f8d7da !important; }
.highlight { background-color: #e3f2fd; font-weight: bold; }
.stat-box { display: inline-block; background: #e8f4fc; padding: 15px; margin: 10px; border-radius: 5px; min-width: 200px; }
.stat-box h3 { margin-top: 0; color: #2980b9; }
.medal { display: inline-block; width: 30px; height: 30px; line-height: 30px; border-radius: 50%; color: white; font-weight: bold; margin-right: 10px; }
.gold { background: #FFD700; }
.silver { background: #C0C0C0; }
.bronze { background: #CD7F32; }
.footer { text-align: center; margin-top: 30px; color: #7f8c8d; font-size: 14px; }
</style>
</head>
<body>
<div class="container">""")
// 报告头部
writer.println(s"""
<h1>学生成绩分析报告</h1>
<div style="text-align: center; color: #666; margin-bottom: 30px;">
生成时间: ${LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"))} |
分析人数: ${scores.length} 人
</div>""")
// 关键统计数据
writer.println("""
<div style="text-align: center;">
<div class="stat-box">
<h3>班级平均分</h3>""")
subjectAverages.foreach { case (subject, avg) =>
writer.println(s"<div>${subject}: <strong>${avg.formatted("%.2f")}</strong> 分</div>")
}
writer.println(s"""
</div>
<div class="stat-box">
<h3>前三名</h3>""")
topThree.zipWithIndex.foreach { case (student, index) =>
val medalClass = index match {
case 0 => "gold"
case 1 => "silver"
case 2 => "bronze"
}
writer.println(s"""<div><span class="medal $medalClass">${index + 1}</span>
${student.name}: <strong>${student.totalScore}</strong> 分</div>""")
}
writer.println("""
</div>
</div>""")
// 成绩表格
writer.println("""
<h2>学生成绩排名表</h2>
<table>
<thead>
<tr>
<th>排名</th>
<th>姓名</th>
<th>语文</th>
<th>数学</th>
<th>英语</th>
<th>总分</th>
<th>平均分</th>
<th>等级</th>
</tr>
</thead>
<tbody>""")
rankedScores.zipWithIndex.foreach { case (student, index) =>
val rowClass = {
if (student.chinese >= 85 && student.math >= 85 && student.english >= 85) "excellent"
else if (student.chinese < 60 || student.math < 60 || student.english < 60) "danger"
else if (student.averageScore < 70) "warning"
else ""
}
val rankClass = index match {
case 0 => "highlight"
case 1 => "highlight"
case 2 => "highlight"
case _ => ""
}
writer.println(s"""
<tr class="$rowClass $rankClass">
<td>${index + 1}</td>
<td><strong>${student.name}</strong></td>
<td>${student.chinese}</td>
<td>${student.math}</td>
<td>${student.english}</td>
<td><strong>${student.totalScore}</strong></td>
<td>${student.averageScore.formatted("%.1f")}</td>
<td>${student.overallGrade}</td>
</tr>""")
}
writer.println("""
</tbody>
</table>""")
// 需要关注的学生
val studentsNeedingAttention = ScoreAnalyzer.findStudentsNeedingAttention(scores)
if (studentsNeedingAttention.nonEmpty) {
writer.println("""
<h2 style="color: #e74c3c;">⚠️ 需要特别关注的学生</h2>
<div style="background: #f8d7da; padding: 15px; border-radius: 5px; margin: 20px 0;">""")
studentsNeedingAttention.foreach { case (student, subjects) =>
writer.println(s"""
<div style="margin: 10px 0;">
<strong>${student.name}</strong>:${subjects.mkString("、")}不及格
(语文${student.chinese}分,数学${student.math}分,英语${student.english}分)
</div>""")
}
writer.println("</div>")
}
// 报告尾部
writer.println(s"""
<div class="footer">
报告生成系统 v1.0 © ${LocalDateTime.now().getYear} 成绩分析系统
</div>
</div>
</body>
</html>""")
} finally {
writer.close()
}
}
}
2.5 主程序入口
package ex_score
import scala.io.Source
import java.io.File
object ScoreAnalysisMain {
def main(args: Array[String]): Unit = {
println("=== 学生成绩分析系统 ===")
println()
// 默认文件路径
val inputFile = if (args.length > 0) args(0) else "score.txt"
val outputTextFile = "成绩分析报告.txt"
val outputHtmlFile = "成绩分析报告.html"
// 检查文件是否存在
if (!new File(inputFile).exists()) {
println(s"错误:文件 '$inputFile' 不存在")
println("请确保 score.txt 文件包含以下格式的内容:")
println("姓名:语文,数学,英语")
println("张伟:87,92,88")
println("李娜:90,85,95")
println("王强:78,90,82")
return
}
try {
// 1. 读取和解析成绩
println(s"正在读取文件: $inputFile")
println("-" * 40)
val scores = ScoreParser.readAndParseScores(inputFile).get
println(s"成功读取 ${scores.length} 名学生成绩")
// 2. 数据验证
val validationErrors = ScoreParser.validateScores(scores)
if (validationErrors.nonEmpty) {
println("警告:发现数据异常:")
validationErrors.foreach(println)
println()
}
// 3. 生成分析报告
println("正在生成分析报告...")
// 文本报告
ScoreReportGenerator.generateTextReport(scores, outputTextFile)
println(s"✓ 文本报告已生成: $outputTextFile")
// HTML报告
ScoreReportGenerator.generateHtmlReport(scores, outputHtmlFile)
println(s"✓ HTML报告已生成: $outputHtmlFile")
// 4. 在控制台显示关键信息
println()
println("=== 关键统计数据 ===")
val stats = ScoreAnalyzer.calculateClassStatistics(scores)
println("各科平均分:")
stats.subjectAverages.foreach { case (subject, avg) =>
println(f" $subject%-4s: ${avg}%.2f 分")
}
println(s"\n班级人数: ${stats.totalStudents}")
println("\n等级分布:")
stats.gradeDistribution.toList.sortBy(_._1).foreach { case (grade, count) =>
println(f" $grade 级: $count 人 (${count * 100.0 / stats.totalStudents}%.1f%%)")
}
println("\n前三名学生:")
stats.topStudents.zipWithIndex.foreach { case (student, index) =>
val medals = List("🥇", "🥈", "🥉")
println(s" ${medals(index)} ${student.name} - 总分: ${student.totalScore} (平均分: ${student.averageScore.formatted("%.1f")})")
}
// 5. 查找需要特别关注的学生
val attentionStudents = ScoreAnalyzer.findStudentsNeedingAttention(scores)
if (attentionStudents.nonEmpty) {
println("\n⚠️ 需要关注的学生(有不及格科目):")
attentionStudents.foreach { case (student, subjects) =>
println(s" ${student.name}: ${subjects.mkString("、")}不及格")
}
}
println()
println("-" * 40)
println("分析完成!")
} catch {
case e: Exception =>
println(s"错误: ${e.getMessage}")
e.printStackTrace()
}
}
}
3. 扩展功能:成绩录入与修改
package ex_score
import java.io.{PrintWriter, FileWriter, File}
import scala.io.Source
object ScoreManager {
// 添加新成绩
def addScore(filePath: String, studentScore: StudentScore): Unit = {
val writer = new FileWriter(filePath, true) // true 表示追加模式
try {
writer.write(s"\n${studentScore.name}:${studentScore.chinese},${studentScore.math},${studentScore.english}")
} finally {
writer.close()
}
}
// 更新成绩
def updateScore(filePath: String, name: String, newScore: StudentScore): Boolean = {
val source = Source.fromFile(filePath, "UTF-8")
val lines = try {
source.getLines().toList
} finally {
source.close()
}
if (lines.isEmpty) return false
// 更新匹配的行
val updatedLines = lines.map { line =>
if (line.startsWith(name + ":")) {
s"${newScore.name}:${newScore.chinese},${newScore.math},${newScore.english}"
} else {
line
}
}
// 写回文件
val writer = new PrintWriter(new File(filePath), "UTF-8")
try {
updatedLines.foreach(writer.println)
true
} finally {
writer.close()
}
}
// 删除成绩
def deleteScore(filePath: String, name: String): Boolean = {
val source = Source.fromFile(filePath, "UTF-8")
val lines = try {
source.getLines().toList
} finally {
source.close()
}
if (lines.isEmpty) return false
val filteredLines = lines.filterNot(_.startsWith(name + ":"))
// 如果删除了数据,更新文件
if (filteredLines.length < lines.length) {
val writer = new PrintWriter(new File(filePath), "UTF-8")
try {
filteredLines.foreach(writer.println)
true
} finally {
writer.close()
}
} else {
false
}
}
// 批量导入成绩
def importScores(filePath: String, newScores: List[StudentScore]): Unit = {
val writer = new FileWriter(filePath, true)
try {
newScores.foreach { score =>
writer.write(s"\n${score.name}:${score.chinese},${score.math},${score.english}")
}
} finally {
writer.close()
}
}
}
4. 使用示例
创建 score.txt 文件:
姓名:语文,数学,英语
张伟:87,92,88
李娜:90,85,95
王强:78,90,82
赵丽:65,72,68
刘明:95,88,92
陈华:82,76,85
运行程序:
// 方式1:直接运行主程序
ScoreAnalysisMain.main(Array())
// 方式2:自定义文件路径
ScoreAnalysisMain.main(Array("D:/成绩数据/期末成绩.txt"))
// 方式3:在命令行运行
// scala ex_score.ScoreAnalysisMain score.txt
5. 功能特点总结
- 完整的成绩分析:总分、平均分、排名、等级
- 数据验证:检查成绩范围,确保数据有效性
- 多种报告格式:文本报告和HTML可视化报告
- 异常处理:使用 Try/Success/Failure 处理可能的错误
- 资源管理:自动关闭文件资源
- 扩展性强:易于添加新的分析功能
- 用户友好:清晰的输出和错误提示
- 支持中文:正确处理中文标点和编码