使用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在实际数据处理中的应用。


相关推荐
diediedei2 小时前
高性能计算通信库
开发语言·c++·算法
雾岛听蓝2 小时前
C++11新特性(可变参数模板、新的类功能、STL中的一些新变化)
开发语言·c++·经验分享·笔记
lxl13072 小时前
学习C++(6)日期类的实现+取地址运算符重载
开发语言·c++·学习
我材不敲代码2 小时前
Python爬虫介绍——简单了解一下爬虫
开发语言·爬虫·python
Howrun7772 小时前
可调用对象
开发语言·c++
2 小时前
java关于引用
java·开发语言
小小码农Come on2 小时前
QT布局介绍
开发语言·qt
晚风吹长发2 小时前
初步了解Linux中的线程概率及线程控制
linux·运维·服务器·开发语言·c++·centos·线程
0白露2 小时前
关闭搜狗输入法右下角广告,可以适用于大多数应用系统通知的广告
windows·bug