鸿蒙应用开发实战【66】— 短信智能解析算法

本文是「号码助手全栈开发系列」第 66 篇,持续更新中…
开源社区:https://openharmonycrossplatform.csdn.net


前言

SmsParser 是号码助手智能识别的核心——它从一段包含多条注册短信的文本中,提取出平台名称和手机号。典型输入是用户从短信列表复制粘贴的一段文字,例如:

【微信】你正在登录微信,验证码 123456。
【招商银行】您尾号 8888 的银行卡充值 100 元。
滴滴出行:13800138000 注册成功。

SmsParser 需要从中识别出:微信(13800138000)招商银行(13800138000)滴滴出行(13800138000)

本篇涵盖:ParseResult 接口设计、PLATFORM_KEYWORDS 长词优先策略、PHONE_PATTERN 正则提取手机号、子串包含消除算法(subsumed)、组合候选生成策略。

在这里插入图片描述


一、数据结构

export interface ParseResult {
  platform: string  // 识别到的平台/应用名称
  phone: string     // 识别到的手机号
  raw: string       // 原始输入文本片段
}

三种组合情况:

platform phone 含义 典型场景
'微信' '13800138000' 同时匹配到平台和手机号 含注册短信的文本
'微信' '' 只匹配到平台 验证码短信无号码
'' '13800138000' 只匹配到手机号 纯号码文本列表

二、关键词匹配策略

2.1 PLATFORM_KEYWORDS

const PLATFORM_KEYWORDS: string[] = [
  // 银行(长词优先)
  '招商银行', '工商银行', '建设银行', '农业银行', '中国银行', '交通银行',
  '浦发银行', '中信银行', '光大银行', '民生银行', '平安银行', '广发银行',
  // ...
  // 运营商
  '中国移动', '中国联通', '中国电信',
  // 支付 / 金融
  '支付宝', '微信支付', '云闪付', '数字人民币',
  // 电商
  '拼多多', '天猫', '淘宝', '京东', '唯品会', '小红书', '得物', '闲鱼',
  // 出行
  '滴滴出行', '哈啰出行', '美团打车',
  // 社交
  '微博', '抖音', '快手', 'QQ',
  // 内容
  '哔哩哔哩', 'B站', '爱奇艺', '优酷视频', '腾讯视频',
  // 办公
  '钉钉', '飞书', '企业微信', '腾讯会议',
  // ... 共 60+ 关键词
  // 微信放在较后(避免"微信支付"被先匹配后再匹配"微信")
  '微信'
]

排列原则

  1. 长词优先招商银行 排在 银行(如果有)前面
  2. 包含词放后微信 放在 微信支付 后面,避免先匹配到短词
  3. 业务分类:虽然顺序不影响匹配,但分组排列便于维护

2.2 长词优先 + 子串包含消除

const platformSet: string[] = []
const matched = new Set<string>()

for (const kw of PLATFORM_KEYWORDS) {
  if (trimmed.includes(kw)) {
    // 检查是否已被更长词包含
    let subsumed = false
    for (const already of matched) {
      if (already.includes(kw)) {  // 如"携程旅行"包含了"携程"
        subsumed = true
        break
      }
    }
    if (!subsumed) {
      platformSet.push(kw)
      matched.add(kw)
    }
  }
}

核心逻辑

  • 遍历排好序的关键词列表
  • 命中关键词后,检查已匹配集合中是否有包含该词的更长词
  • 有则跳过(subsumed),无则加入结果集

例如输入文本包含"携程旅行":

  1. 遍历到 携程旅行 → 命中,matched = {'携程旅行'}
  2. 遍历到 携程 → 也已命中,但 '携程旅行'.includes('携程') → subsumed 跳过

三、手机号提取

const PHONE_PATTERN = '(1[3-9]\\d{9})'

const phoneReg = new RegExp(PHONE_PATTERN, 'g')
let phoneMatch: RegExpExecArray | null = phoneReg.exec(trimmed)
while (phoneMatch !== null) {
  const phone = phoneMatch[1]
  if (!phoneSet.includes(phone)) {
    phoneSet.push(phone)
  }
  phoneMatch = phoneReg.exec(trimmed)
}

说明

  • 1[3-9]\d{9} 覆盖中国大陆目前所有手机号段(13x-19x)
  • 使用 exec + 循环 + g 标志提取所有匹配
  • 手动去重(phoneSet.includes),保留首次出现顺序

四、组合候选生成

const results: ParseResult[] = []

if (platformSet.length > 0 && phoneSet.length > 0) {
  // 笛卡尔积:每个平台 × 每个手机号
  for (const platform of platformSet) {
    for (const phone of phoneSet) {
      results.push({ platform, phone, raw: trimmed })
    }
  }
} else if (platformSet.length > 0) {
  for (const platform of platformSet) {
    results.push({ platform, phone: '', raw: trimmed })
  }
} else {
  for (const phone of phoneSet) {
    results.push({ platform: '', phone, raw: trimmed })
  }
}

三种分支

条件 结果 典型场景
有平台 + 有号码 笛卡尔积组合 短信包含平台名和手机号
有平台 + 无号码 仅平台名 短信仅包含平台信息
无平台 + 有号码 仅手机号 纯号码文本
两者皆无 空数组 无关文本

五、完整解析示例

输入文本:

【建设银行】您尾号8888的信用卡消费100元。【支付宝】13800138000登录验证码。
【携程旅行】您预订的酒店已确认。[携程]
【滴滴出行】13800138000行程已结束。

解析过程:

1. 手机号提取 → ['13800138000']
2. 平台名提取(长词优先):
   - '建设银行' ✓(matched: {建设银行})
   - '支付宝' ✓(matched: {建设银行, 支付宝})
   - '携程旅行' ✓(matched: {建设银行, 支付宝, 携程旅行})
   - '滴滴出行' ✓(matched: {建设银行, 支付宝, 携程旅行, 滴滴出行})
   - '携程' ✗(被 '携程旅行'.includes('携程') 排除)
3. 笛卡尔积 → 4 条结果:
   - 建设银行 / 13800138000
   - 支付宝 / 13800138000
   - 携程旅行 / 13800138000
   - 滴滴出行 / 13800138000

小结

要点 说明
关键词列表 60+ 平台按长词优先排列
子串消除 already.includes(kw) 避免短词重复
手机号提取 1[3-9]\d{9} 正则 + exec 循环去重
组合策略 笛卡尔积(有平台有号码)/ 仅平台 / 仅号码
返回结果 ParseResult[] 包含 platform + phone + raw
匹配度提示 UI 层固定显示"匹配度 96%"(模拟文本)

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐