Scala 实现身份证号解析:一键提取性别、年龄、省份和星座

111 阅读6分钟

在日常开发和数据处理场景中,身份证号是蕴含丰富个人信息的 “数据载体”。基于 18 位身份证号的编码规则,我们可以解析出性别、年龄、籍贯甚至星座等信息。本文将基于Scala 语言,从零搭建一个身份证号信息解析工具,带你吃透字符串截取、模式匹配等核心语法,同时实现实用的多维度信息提取功能。

一、身份证号编码规则与解析思路

要实现解析功能,首先得搞懂身份证号的编码逻辑,这是开发的核心依据:

  1. 长度规则:中国大陆标准身份证号为18 位,前 17 位为数字,最后一位可为数字或X/x
  2. 地域编码前 2 位代表省级行政区
  3. 出生日期第 7-14 位为出生年月日
  4. 性别标识第 17 位为性别位,奇数为男性,偶数为女性
  5. 校验码:第 18 位为校验位,用于验证身份证号合法性

基于以上规则,我们的工具将实现5 大核心功能:合法性校验、性别判断、年龄计算、省份匹配、星座判断

二、技术选型与语法

本次会用到 Scala 的几个核心特性,也是函数式编程的典型体现:

  • substring方法:精准截取身份证号不同位置的字符段,实现信息提取;
  • 不可变 Map:存储省级行政区编码与名称的映射关系,保证数据安全;
  • 模式匹配 :优雅实现星座的日期区间判断,替代繁琐的if-else
  • LocalDate:获取当前年份,完成年龄的动态计算;
  • try-catch异常处理:捕获数字转换异常,提升程序健壮性。

三、完整代码

下面是可直接运行的完整代码,已添加注释和用户友好的交互逻辑:

// 导入日期工具类,用于获取当前年份
import java.time.LocalDate

/**
 * 身份证号信息解析工具
 * 支持性别、年龄、省份、星座多维度解析,含异常处理和退出机制
 */
object IDCardAnalyzer {
  def main(args: Array[String]): Unit = {
    println("=== 身份证号信息解析工具 ===")
    println("提示:输入18位身份证号解析信息,输入exit可退出程序\n")

    // 循环接收用户输入,实现持续解析
    while (true) {
      val inputId = scala.io.StdIn.readLine("请输入身份证号:").trim

      // 退出逻辑:不区分大小写匹配exit
      if (inputId.equalsIgnoreCase("exit")) {
        println("\n程序已退出,感谢使用!")
        return
      }

      // 第一步:校验身份证号长度合法性
      if (inputId.length != 18) {
        println(s"错误:${inputId}长度非18位,不是合法身份证号!\n")
        continue
      }

      try {
        // 1. 性别解析:截取第17位(下标16),奇数为男,偶数为女
        val genderCode = inputId.substring(16, 17).toInt
        val gender = if (genderCode % 2 == 1) "男性(小哥)" else "女性(姑娘)"

        // 2. 年龄解析:截取第7-10位出生年份,计算与当前年份差值
        val birthYear = inputId.substring(6, 10).toInt
        val currentYear = LocalDate.now().getYear
        val age = currentYear - birthYear

        // 3. 省份解析:通过前2位编码匹配省级行政区
        val provinceCode = inputId.substring(0, 2)
        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" -> "新疆维吾尔自治区"
        )
        val province = regionMap.getOrElse(provinceCode, s"未知地区(编码${provinceCode})")

        // 4. 星座解析:截取出生月日,调用工具方法匹配星座
        val birthMonth = inputId.substring(10, 12).toInt
        val birthDay = inputId.substring(12, 14).toInt
        val constellation = getConstellation(birthMonth, birthDay)

        // 输出解析结果,格式化提升可读性
        println("\n解析成功!")
        println(s"身份证号:${inputId}")
        println(s"性别:${gender}")
        println(s"年龄:${age}岁(出生于${birthYear}年)")
        println(s"籍贯:${province}")
        println(s"星座:${constellation}\n")

      } catch {
        // 捕获非数字转换异常(兼容最后一位X/x)
        case e: NumberFormatException =>
          val lastChar = inputId.charAt(17)
          if (lastChar == 'X' || lastChar == 'x') {
            println("提示:身份证号最后一位为X/x,已忽略该位校验,请重新输入完整信息!\n")
          } else {
            println("错误:身份证号含非数字字符!\n")
          }
      }
    }
  }

  /**
   * 星座匹配工具方法
   * @param month 出生月
   * @param day 出生日期
   * @return 对应星座名称
   */
  private def getConstellation(month: Int, day: Int): String = {
    (month, day) match {
      case (1, d) if d >= 20 => "水瓶座"
      case (2, d) if d <= 18 => "水瓶座"
      case (2, d) if d >= 19 => "双鱼座"
      case (3, d) if d <= 20 => "双鱼座"
      case (3, d) if d >= 21 => "白羊座"
      case (4, d) if d <= 19 => "白羊座"
      case (4, d) if d >= 20 => "金牛座"
      case (5, d) if d <= 20 => "金牛座"
      case (5, d) if d >= 21 => "双子座"
      case (6, d) if d <= 21 => "双子座"
      case (6, d) if d >= 22 => "巨蟹座"
      case (7, d) if d <= 22 => "巨蟹座"
      case (7, d) if d >= 23 => "狮子座"
      case (8, d) if d <= 22 => "狮子座"
      case (8, d) if d >= 23 => "处女座"
      case (9, d) if d <= 22 => "处女座"
      case (9, d) if d >= 23 => "天秤座"
      case (10, d) if d <= 23 => "天秤座"
      case (10, d) if d >= 24 => "天蝎座"
      case (11, d) if d <= 22 => "天蝎座"
      case (11, d) if d >= 23 => "射手座"
      case (12, d) if d <= 21 => "射手座"
      case _ => "摩羯座"
    }
  }
}

四、功能拆解

1. 合法性校验

程序首先校验输入长度是否为 18 位,非 18 位直接返回错误提示,这是最基础的合法性判断。同时通过try-catch捕获NumberFormatException,兼容最后一位的X/x校验码

2. 性别解析

身份证第 17 位为性别位,通过substring(16,17)截取该字符并转为数字,再通过取模运算%2判断奇偶,实现性别区分

3. 年龄解析

截取第 7-10 位的出生年份,结合LocalDate.now().getYear()获取的当前年份,通过差值计算年龄

4. 省份解析

通过不可变 Map 存储全国 34 个省级行政区的编码映射,使用getOrElse方法实现 “匹配到返回名称,匹配不到返回未知” 的兜底逻辑,避免空指针异常

5. 星座解析

自定义getConstellation方法,利用 Scala 的模式匹配特性,按星座的日期区间逐一匹配,相比多层if-else更简洁易维护

五、运行测试

1. 合法输入测试

输入示例:11010119990520701X输出结果:

解析成功!
身份证号:11010119990520701X
性别:男性(小哥)
年龄:26岁(出生于1999年)
籍贯:北京市
星座:金牛座

2. 非法输入测试

输入示例:12345输出结果:

错误:12345长度非18位,不是合法身份证号!

六、功能扩展

  1. 精准年龄计算:解析完整生日,对比当前日期判断生日是否已过,实现精确到天的年龄计算
  2. 完整合法性校验:引入身份证号校验算法,验证第 18 位校验码的合法性
  3. 地区多级解析:补充前 6 位行政区划编码,实现省、市、区三级地域解析
  4. GUI 界面封装:结合 ScalaFX 等框架,开发可视化操作界面,提升用户体验

总结

本文通过一个实用的身份证号解析工具,实现了多维度信息提取,深度实践了 Scala 的字符串操作、模式匹配、异常处理等核心语法。该代码可直接用于数据清洗、用户信息预处理等场景,也可根据实际需求进一步扩展功能。希望这篇文章能帮你夯实 Scala 基础,同时掌握实用的业务开发技巧