使用Scala实现手机号码归属地查询系统

使用Scala实现手机号码归属地查询系统

项目概述

在现代社会,手机已经成为人们生活中不可或缺的一部分。面对陌生来电,查询号码归属地可以帮助我们识别来电性质,避免骚扰和诈骗。本文将介绍如何使用Scala构建一个完整的手机号码归属地查询系统。

一、项目准备

1.1 数据模型设计

首先,我们需要定义一个数据模型来表示手机号码归属地信息:

scala 复制代码
case class PhoneNumberInfo(
    id: Long,           // 编号
    segment: String,    // 号码段(前7位)
    province: String,   // 省份
    city: String,       // 城市
    carrier: String,    // 运营商
    areaCode: String,   // 区号
    zipCode: String     // 邮编
)

1.2 数据加载

我们从CSV文件中加载数据,这里假设数据已保存为phone_numbers.csv

scala 复制代码
import scala.io.Source
import scala.collection.mutable.ListBuffer

object PhoneNumberDataLoader {
    
    def loadData(filePath: String): List[PhoneNumberInfo] = {
        val buffer = ListBuffer[PhoneNumberInfo]()
        
        val source = Source.fromFile(filePath, "UTF-8")
        try {
            for (line <- source.getLines()) {
                val fields = line.split(",")
                if (fields.length == 7) {
                    val info = PhoneNumberInfo(
                        fields(0).trim.toLong,
                        fields(1).trim,
                        fields(2).trim,
                        fields(3).trim,
                        fields(4).trim,
                        fields(5).trim,
                        fields(6).trim
                    )
                    buffer += info
                }
            }
        } finally {
            source.close()
        }
        
        buffer.toList
    }
}

二、任务实现

2.1 任务一:识别号码类型

我们可以根据号码的前三位来判断运营商类型:

scala 复制代码
object PhoneNumberAnalyzer {
    
    // 定义运营商前缀
    private val carrierPrefixes = Map(
        "中国移动" -> List("134", "135", "136", "137", "138", "139", "147", "150", 
                          "151", "152", "157", "158", "159", "178", "182", "183", 
                          "184", "187", "188"),
        "中国联通" -> List("130", "131", "132", "145", "155", "156", "166", "175", 
                          "176", "185", "186"),
        "中国电信" -> List("133", "149", "153", "173", "177", "180", "181", "189", "199")
    )
    
    /**
     * 根据手机号码识别运营商
     */
    def identifyCarrier(phoneNumber: String): String = {
        if (phoneNumber.length < 3) {
            return "未知运营商"
        }
        
        val prefix = phoneNumber.substring(0, 3)
        
        carrierPrefixes.find { case (carrier, prefixes) =>
            prefixes.contains(prefix)
        } match {
            case Some((carrier, _)) => carrier
            case None => "未知运营商"
        }
    }
    
    /**
     * 验证手机号码格式
     */
    def validatePhoneNumber(phoneNumber: String): Boolean = {
        val pattern = "^1[3-9]\\d{9}$".r
        pattern.matches(phoneNumber)
    }
}

2.2 任务二:统计广州号码段数量

scala 复制代码
object StatisticsAnalyzer {
    
    /**
     * 统计指定城市的号码段数量
     */
    def countSegmentsByCity(data: List[PhoneNumberInfo], city: String): Int = {
        data.count(_.city == city)
    }
    
    /**
     * 统计广州号码段(包含详细统计信息)
     */
    def analyzeGuangzhouSegments(data: List[PhoneNumberInfo]): Map[String, Int] = {
        val guangzhouData = data.filter(_.city == "广州")
        
        // 按运营商统计
        val carrierStats = guangzhouData.groupBy(_.carrier)
            .map { case (carrier, items) => 
                s"${carrier}广州号码段" -> items.length 
            }
        
        // 总数
        val total = Map("广州总号码段数" -> guangzhouData.length)
        
        total ++ carrierStats
    }
}

2.3 任务三:根据归属地对手机号码段分组

scala 复制代码
object GroupingAnalyzer {
    
    /**
     * 按省份分组
     */
    def groupByProvince(data: List[PhoneNumberInfo]): Map[String, List[PhoneNumberInfo]] = {
        data.groupBy(_.province)
    }
    
    /**
     * 按城市分组
     */
    def groupByCity(data: List[PhoneNumberInfo]): Map[String, List[PhoneNumberInfo]] = {
        data.groupBy(_.city)
    }
    
    /**
     * 按省份和运营商双重分组
     */
    def groupByProvinceAndCarrier(data: List[PhoneNumberInfo]): 
        Map[String, Map[String, List[PhoneNumberInfo]]] = {
        
        data.groupBy(_.province)
            .map { case (province, items) =>
                province -> items.groupBy(_.carrier)
            }
    }
    
    /**
     * 获取省份号码段统计
     */
    def getProvinceStatistics(data: List[PhoneNumberInfo]): List[(String, Int)] = {
        groupByProvince(data)
            .map { case (province, items) => (province, items.length) }
            .toList
            .sortBy(-_._2)  // 按数量降序排序
    }
}

2.4 任务四:手机号码归属地信息查询程序

scala 复制代码
object PhoneNumberQuerySystem {
    
    // 使用Map提高查询效率
    private var segmentMap: Map[String, PhoneNumberInfo] = Map()
    
    /**
     * 初始化查询系统
     */
    def initialize(data: List[PhoneNumberInfo]): Unit = {
        segmentMap = data.map(info => info.segment -> info).toMap
    }
    
    /**
     * 查询手机号码归属地
     */
    def queryByPhoneNumber(phoneNumber: String): Option[PhoneNumberInfo] = {
        if (!PhoneNumberAnalyzer.validatePhoneNumber(phoneNumber)) {
            return None
        }
        
        val segment = phoneNumber.substring(0, 7)
        segmentMap.get(segment)
    }
    
    /**
     * 模糊查询(按城市查询所有号码段)
     */
    def queryByCity(data: List[PhoneNumberInfo], city: String): List[PhoneNumberInfo] = {
        data.filter(_.city.contains(city))
    }
    
    /**
     * 按运营商查询
     */
    def queryByCarrier(data: List[PhoneNumberInfo], carrier: String): List[PhoneNumberInfo] = {
        data.filter(_.carrier == carrier)
    }
    
    /**
     * 交互式查询界面
     */
    def interactiveQuery(data: List[PhoneNumberInfo]): Unit = {
        println("=== 手机号码归属地查询系统 ===")
        println("1. 按手机号码查询")
        println("2. 按城市查询")
        println("3. 按运营商查询")
        println("4. 退出")
        
        var continue = true
        
        while (continue) {
            print("\n请选择查询方式 (1-4): ")
            val choice = scala.io.StdIn.readLine()
            
            choice match {
                case "1" =>
                    print("请输入手机号码: ")
                    val phone = scala.io.StdIn.readLine()
                    queryByPhoneNumber(phone) match {
                        case Some(info) =>
                            println(s"\n查询结果:")
                            println(s"号码段: ${info.segment}")
                            println(s"归属地: ${info.province}省${info.city}市")
                            println(s"运营商: ${info.carrier}")
                            println(s"区号: ${info.areaCode}")
                            println(s"邮编: ${info.zipCode}")
                        case None =>
                            println("未找到该号码的归属地信息")
                    }
                    
                case "2" =>
                    print("请输入城市名称: ")
                    val city = scala.io.StdIn.readLine()
                    val results = queryByCity(data, city)
                    if (results.nonEmpty) {
                        println(s"\n${city}的号码段 (共${results.length}个):")
                        results.take(10).foreach { info =>
                            println(s"  ${info.segment} - ${info.carrier}")
                        }
                        if (results.length > 10) {
                            println(s"  ... 还有${results.length - 10}个")
                        }
                    } else {
                        println("未找到该城市的号码段")
                    }
                    
                case "3" =>
                    println("请选择运营商:")
                    println("1. 中国移动")
                    println("2. 中国联通")
                    println("3. 中国电信")
                    print("选择: ")
                    val carrierChoice = scala.io.StdIn.readLine()
                    val carrier = carrierChoice match {
                        case "1" => "中国移动"
                        case "2" => "中国联通"
                        case "3" => "中国电信"
                        case _ => "未知"
                    }
                    if (carrier != "未知") {
                        val results = queryByCarrier(data, carrier)
                        println(s"\n${carrier}号码段统计:")
                        val stats = results.groupBy(_.province)
                            .map { case (province, items) => 
                                s"  ${province}: ${items.length}个" 
                            }
                            .toList
                            .sorted
                        stats.foreach(println)
                    }
                    
                case "4" =>
                    println("感谢使用,再见!")
                    continue = false
                    
                case _ =>
                    println("无效选择,请重新输入")
            }
        }
    }
}

三、完整应用示例

scala 复制代码
object MainApplication {
    
    def main(args: Array[String]): Unit = {
        
        // 1. 加载数据
        println("正在加载数据...")
        val data = PhoneNumberDataLoader.loadData("phone_numbers.csv")
        println(s"数据加载完成,共${data.length}条记录")
        
        // 2. 初始化查询系统
        PhoneNumberQuerySystem.initialize(data)
        
        // 3. 执行各种分析任务
        
        // 任务1: 识别号码类型示例
        val testNumber = "13012345678"
        println(s"\n=== 任务1: 识别号码类型 ===")
        println(s"测试号码: $testNumber")
        println(s"运营商: ${PhoneNumberAnalyzer.identifyCarrier(testNumber)}")
        
        // 任务2: 统计广州号码段数量
        println(s"\n=== 任务2: 统计广州号码段 ===")
        val guangzhouStats = StatisticsAnalyzer.analyzeGuangzhouSegments(data)
        guangzhouStats.foreach { case (key, value) =>
            println(s"$key: $value")
        }
        
        // 任务3: 根据归属地分组
        println(s"\n=== 任务3: 归属地分组统计 ===")
        val topProvinces = GroupingAnalyzer.getProvinceStatistics(data).take(5)
        println("号码段数量前5的省份:")
        topProvinces.foreach { case (province, count) =>
            println(s"  $province: $count 个")
        }
        
        // 任务4: 启动交互式查询
        println(s"\n=== 任务4: 交互式查询系统 ===")
        PhoneNumberQuerySystem.interactiveQuery(data)
    }
}

四、性能优化建议

4.1 使用缓存提高查询性能

scala 复制代码
object QueryCache {
    
    private val cache = scala.collection.mutable.Map[String, PhoneNumberInfo]()
    
    def getOrElseUpdate(segment: String, 
                       compute: => Option[PhoneNumberInfo]): Option[PhoneNumberInfo] = {
        cache.get(segment) match {
            case Some(info) => Some(info)
            case None =>
                compute match {
                    case Some(info) => 
                        cache.put(segment, info)
                        Some(info)
                    case None => None
                }
        }
    }
    
    def clear(): Unit = cache.clear()
}

4.2 使用并行处理加速统计

scala 复制代码
object ParallelProcessor {
    
    /**
     * 并行统计各省份号码段数量
     */
    def parallelProvinceStatistics(data: List[PhoneNumberInfo]): Map[String, Int] = {
        data.par
            .groupBy(_.province)
            .map { case (province, items) => province -> items.length }
            .seq
            .toMap
    }
    
    /**
     * 并行查找多个号码
     */
    def batchQuery(phoneNumbers: List[String], 
                   data: List[PhoneNumberInfo]): List[(String, Option[PhoneNumberInfo])] = {
        phoneNumbers.par.map { phone =>
            phone -> PhoneNumberQuerySystem.queryByPhoneNumber(phone)
        }.seq.toList
    }
}

五、测试用例

scala 复制代码
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class PhoneNumberQuerySpec extends AnyFlatSpec with Matchers {
    
    val testData = List(
        PhoneNumberInfo(1, "1300000", "山东", "济南", "中国联通", "0531", "250000"),
        PhoneNumberInfo(2, "1300001", "江苏", "常州", "中国联通", "0519", "213000"),
        PhoneNumberInfo(3, "1380000", "广东", "广州", "中国移动", "020", "510000")
    )
    
    "PhoneNumberAnalyzer" should "正确识别运营商" in {
        PhoneNumberAnalyzer.identifyCarrier("13012345678") shouldBe "中国联通"
        PhoneNumberAnalyzer.identifyCarrier("13812345678") shouldBe "中国移动"
        PhoneNumberAnalyzer.identifyCarrier("13312345678") shouldBe "中国电信"
    }
    
    "StatisticsAnalyzer" should "正确统计广州号码段" in {
        StatisticsAnalyzer.countSegmentsByCity(testData, "广州") shouldBe 1
    }
    
    "PhoneNumberQuerySystem" should "正确查询手机号码归属地" in {
        PhoneNumberQuerySystem.initialize(testData)
        val result = PhoneNumberQuerySystem.queryByPhoneNumber("13000001234")
        result shouldBe defined
        result.get.city shouldBe "济南"
    }
}

六、总结

通过这个项目,我们实现了:

  1. 数据模型设计:使用case class定义清晰的数据结构
  2. 号码类型识别:基于号码前缀的运营商识别
  3. 数据统计:按城市、运营商等多维度统计分析
  4. 分组查询:灵活的归属地分组功能
  5. 交互式查询:用户友好的命令行界面

这个系统不仅可以帮助用户查询手机号码归属地,还可以用于数据分析,比如:

  • 运营商市场份额分析
  • 地区通信资源分布
  • 号码段使用情况统计

Scala的函数式编程特性和强大的集合操作能力,使得这类数据处理任务变得简洁高效。希望这个项目能帮助你更好地理解Scala在实际数据处理中的应用。


相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜12 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa12 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio