Scala 继承与文件读写实战:从身份证信息提取到成绩统计

39 阅读5分钟

一、继承的概念和基本语法

定义

继承是面向对象编程的三大特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码的复用和扩展。

好处

  1. 代码复用:避免重复编写相同的代码
  2. 扩展性:可以在不修改原有类的基础上添加新功能
  3. 多态支持:为多态的实现提供了基础

语法

class 父类 {
  // 父类的成员
}

class 子类 extends 父类 {
  // 子类的成员
}

二、继承的方法重写

格式

override def 方法名(参数列表): 返回类型 = {
  // 重写的实现
}

特点

  1. 使用 override 关键字明确表示重写
  2. 重写的方法必须与父类方法具有相同的签名
  3. Scala 中默认方法是可以被重写的(除非标记为 final)

三、文件读写功能实战

1. 如何读入文件的内容?

Scala 提供了多种读取文件的方式,最常用的是使用 Source 对象:

import scala.io.Source

val source = Source.fromFile("filename.txt", "UTF-8")
val lines = source.getLines().toList
source.close()

2. 如何分割字符串或为数组?

使用 split 方法:

val str = "apple,banana,orange"
val arr = str.split(",")  // 返回 Array[String]

3. 如何对Map中的元素进行排序?

// 按值排序
val sortedMap = map.toSeq.sortBy(_._2).toMap

// 按键排序
val sortedMap = map.toSeq.sortBy(_._1).toMap

// 按值降序排序
val sortedMap = map.toSeq.sortBy(-_._2).toMap

4. 如何写入内容到文件?

使用 java.io.PrintWriter

import java.io._

val writer = new PrintWriter(new File("output.txt"))
writer.write("Hello, Scala!")
writer.close()

四、实训案例一:身份证信息提取系统

案例:身份证信息分析程序

代码

package 实训内容
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit

/*
* 任务: 从键盘输入一个身份证号。
* 写程序,提取信息
*      1.性别
**/
object daydream {

  // 身份证前两位地区映射
  val regionMap: Map[String, String] = Map(
    "11" -> "北京市", "12" -> "天津市", "13" -> "河北省", "14" -> "山西省", "15" -> "内蒙古自治区",
    "21" -> "辽宁省", "22" -> "吉林省", "23" -> "黑龙江省",
    "31" -> "上海市", "32" -> "江苏省", "33" -> "浙江省", "34" -> "安徽省", "35" -> "福建省",
    "36" -> "江西省", "37" -> "山东省", "41" -> "河南省", "42" -> "湖北省", "43" -> "湖南省",
    "44" -> "广东省", "45" -> "广西壮族自治区", "46" -> "海南省", "50" -> "重庆市", "51" -> "四川省",
    "52" -> "贵州省", "53" -> "云南省", "54" -> "西藏自治区", "61" -> "陕西省", "62" -> "甘肃省",
    "63" -> "青海省", "64" -> "宁夏回族自治区", "65" -> "新疆维吾尔自治区", "71" -> "台湾省",
    "81" -> "香港特别行政区", "82" -> "澳门特别行政区"
  )

  // 星座日期范围定义
  private val zodiacRanges = List(
    ((3, 21), (4, 19), "白羊座"),
    ((4, 20), (5, 20), "金牛座"),
    ((5, 21), (6, 21), "双子座"),
    ((6, 22), (7, 22), "巨蟹座"),
    ((7, 23), (8, 22), "狮子座"),
    ((8, 23), (9, 22), "处女座"),
    ((9, 23), (10, 23), "天秤座"),
    ((10, 24), (11, 22), "天蝎座"),
    ((11, 23), (12, 21), "射手座"),
    ((12, 22), (1, 19), "摩羯座"),
    ((1, 20), (2, 18), "水瓶座"),
    ((2, 19), (3, 20), "双鱼座")
  )

  // 函数
  def getZodiacPersonality(zodiac: String): String = {
    zodiac match {
      case "白羊座" => "热情勇敢、行动力强"
      case "金牛座" => "稳重务实、有耐心"
      case "双子座" => "聪明机智、好奇心强"
      case "巨蟹座" => "情感细腻、家庭观念强"
      case "狮子座" => "自信大方、有领导力"
      case "处女座" => "细心谨慎、追求完美"
      case "天秤座" => "优雅和谐、公正平衡"
      case "天蝎座" => "深沉神秘、直觉敏锐"
      case "射手座" => "乐观开朗、热爱自由、冒险精神"
      case "摩羯座" => "务实稳重、责任感强、目标明确"
      case "水瓶座" => "独立思考、创新思维、友善博爱"
      case "双鱼座" => "温柔浪漫、富有同情心、想象力丰富"
      case _ => "性格特征未知"
    }
  }

  def main(args: Array[String]): Unit = {
    //    println("请输入身份证号码:")
    //    val idCard = scala.io.StdIn.readLine().trim
    val idCard = "420321199802195585"

    if (validateIdCard(idCard)) {
      analyzeIdCard(idCard)
    } else {
      println("身份证号码格式错误!")
    }
  }

  /**
   * 根据月份和日期判断星座
   * @param month 月份 (1-12)
   * @param day 日期 (1-31)
   * @return 星座名称
   */
  def getZodiac(month: Int, day: Int): String = {
    require(month >= 1 && month <= 12, "月份必须在1-12之间")
    require(day >= 1 && day <= 31, "日期必须在1-31之间")

    // 将日期转换为统一格式进行比较
    val dateValue = month * 100 + day

    zodiacRanges.find { case ((startMonth, startDay), (endMonth, endDay), _) =>
      val startValue = startMonth * 100 + startDay
      val endValue = endMonth * 100 + endDay

      if (startMonth <= endMonth) {
        // 正常区间(同一年内)
        dateValue >= startValue && dateValue <= endValue
      } else {
        // 跨年区间(摩羯座、水瓶座、双鱼座)
        dateValue >= startValue || dateValue <= endValue
      }
    }.map(_._3).getOrElse("未知星座")
  }

  def validateIdCard(idCard: String): Boolean = {
    idCard.matches("^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$")
  }

  def analyzeIdCard(idCard: String): Unit = {
    // 提取地区代码
    val regionCode = idCard.substring(0, 2)
    val region = regionMap.getOrElse(regionCode, "未知地区")

    // 提取性别(第17位,奇数为男,偶数为女)
    val genderDigit = idCard.charAt(16).toString.toInt
    val gender = if (genderDigit % 2 == 1) "男" else "女"

    // 提取生日
    val birthdayStr = idCard.substring(6, 14)
    val formatter = DateTimeFormatter.ofPattern("yyyyMMdd")
    val birthday = LocalDate.parse(birthdayStr, formatter)
    val formattedBirthday = birthday.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))

    // 计算年龄
    val currentDate = LocalDate.now()
    val age = ChronoUnit.YEARS.between(birthday, currentDate).toInt

    // 提取月份
    val month = idCard.substring(10, 12).toInt
    // 提取日期
    val day = idCard.substring(12, 14).toInt

    val zodiac =  getZodiac(month, day)

    val personality= getZodiacPersonality(zodiac)

    // 输出结果
    println("身份证信息分析结果:")
    println("籍贯:" + region)
    println("性别:" + gender)
    println("年龄:" + age + "岁")
    println("生日:" + formattedBirthday)
    println("星座:" + zodiac)
    println("性格:" + personality)
  }
}

代码结果

Snipaste_2025-12-16_08-55-15.png

代码分析

这个身份证信息提取系统展示了 Scala 面向对象编程和函数式编程的结合:

  1. 正则表达式验证validateIdCard 方法使用正则表达式验证身份证格式
  2. 模式匹配getZodiacPersonality 方法使用模式匹配为不同星座返回不同的性格描述
  3. 日期处理:使用 Java 8 的 LocalDateDateTimeFormatter 进行日期解析和格式化
  4. 映射查找:通过 regionMap 映射表将地区代码转换为地区名称
  5. 函数组合:多个小函数组合完成复杂的身份证信息提取任务

关键算法说明:

  • 星座判断算法:将月份和日期转换为数值(月份×100+日期),便于区间比较
  • 年龄计算:使用 ChronoUnit.YEARS.between 精确计算年龄
  • 性别判断:根据身份证第17位数字的奇偶性判断性别