使用正则表达式解析短信内容

通常,Android手机自带的短信软件都可以将解析内容并且提取出里面的关键信息展示成卡片的样式或者提供让用户进一步操作的按钮。例如在坚果手机上验证码短信会展示成这样:

信用卡的消费短信会展示成这样:

这篇文章将讨论如何基于正则表达式实现类似上述的功能。

一、需求分析

需要在卡片上展示标题 标题可能是固定的比如像图上的信用卡消费或者是验证码,也可能是不固定,比如说来自短信的内容

卡片上将会醒目的展示出最核心的内容和类型,例如图上验证码信息和金额信息

其余的次重要信息按照键值对的形式分别展示出来,例如消费短信中的 账号、短信、时间等信息。

是否需要展示短信的原文内容,这个部分考虑支持可配。让使用者去决定当前的短信卡片是否要展示原文。

短信卡片展示的下一步动作,我们暂时仅考虑支持复制卡片上最醒目的信息,例如验证码,或者是消费金额(尽管好像没什么卵用)

二、关于正则

在整个短信解析的正则中最重要的就是捕获组的概念。

我们首先来看一下京东快递柜的短信提醒

【京东快递柜】凭取件码 26558117 一个小区 京东快递柜取件,电话 12345678910 ,关注京东快递公众号扫码取件。

在这个短信中除了加粗的部分之外,其余的内容都是固定的,所以我们很容易的可以写出下面这样的正则表达式

【京东快递柜】凭取件码\d{8}到.*?京东快递柜取件,电话\d{11},关注京东快递公众号扫码取件。

我们需要在卡片上展示短信中的内容,我们要用到捕获命名组,我们加上命名捕获组后正则表达式会是下面这样

【京东快递柜】凭取件码(?<code>\d{8})到(?<location>.*?)京东快递柜取件,电话(?<phone>\d{11}),关注京东快递公众号扫码取件。

我们就可以在代码中通过名称组名来获取对应的内容。

三、规则数据存储

基于以上的需求我们就可以设计出如下的结构来存储一个短信内容的解析规则,毕竟一个正则是没有办法解析所有的短信的。

    filter       '过滤器,如果短信的内容符合过滤的规则,则使用 regex记录的正则来提取数据',
    regex        '用于数据提取的正则表达式',
    group_names  '内容分组的名称 使用 , 分割 ,例如记录上述京东快递柜短信提醒组的名称: code|取件码,location|位置,phone|联系电话'
    sort         '排序,规则的先后顺序,满足一个则后面的规则默认不在参与处理',
    title        '标题,标题可以写规定的内容,也可以引用捕获组的内容 ,例如 #P#code 使用捕获到组名为code的文本作为短信卡片的标题'
    show_content '是否显示原文'
    copy_main    '是否可以复制名为 mian 组捕获到的内容到剪贴板'

在group_names 字段中,默认显示在卡片最醒目位置的文本的捕获组的名称命名为 main,例如快递柜通知短信要显示的最醒目的内容是取件码,那么取件码的组名就是 main

四、代码实现

有了上面的结构,对短信的解析就可以简单处理为让短信按照定义的sort字段的顺序,逐个匹配 filter记录的正则表达式,如果通过,则使用regex记录的正则提取数据,并且group_name将内容与记录的标题对应,给页面去渲染就可以了。

代码示例如下:

function resolveSMSContent(content, extractRules) {
    for (let i = 0; i < extractRules.length; i++) {
        let extractRule = extractRules[i];
        let filter = eval(extractRule.filter);
        let exec;
        if (filter.test(content)) {
            let patternStr = eval(extractRule.regex)
            let mainContent;
            let title = extractRule.title;
            let groupNames = extractRule.groupNames.split(",");
            let values = [];
            patternStr = eval(patternStr)
            exec = patternStr.exec(content);
            for (let i = 0; i < groupNames.length; i++) {
                let groupName = groupNames[i].split("|")[0];
                let param = {
                    key: groupNames[i].split("|")[1],
                    text: exec.groups[groupName]
                if (extractRule.title.startsWith("#P#") && extractRule.title.replace("#P#", "") === groupName) {
                    title = exec.groups[groupName];
                    continue;
                if (groupNames[i].split("|")[0] === 'main') {
                    mainContent = param;
                    continue;
                values.push(param)
            let card = {
                title: title,
                mainContent: mainContent,
                content: content,
                param: values,
                copyMain: extractRule.copyMain,
                showContent: extractRule.showContent
            if (card.copyMain) {
                writeContentToClipBoard(card.mainContent.text);
            console.log(card);
            return card;
    return '';

解析之后的数据:

"title": "京东快递柜", //卡片标题 "mainContent": { "key": "取件码", //展示关键信息的名称 "text": "26558117" 关键信息的内容 "content": "【京东快递柜】凭取件码26558117到一个小区京东快递柜取件,电话12345678910,关注京东快递公众号扫码取件。", //短信原文 "param": [ //次重要参数 "key": "联系电话", //名称 "text": "12345678910" //内容 "key": "位置", "text": "一个小区" "copyMain": false //是否可以复制关键信息

有了上面的数据就可以很容易的展示成下面这样:

源代码:Util.js#resolveSMSContent

代码来自于Blue bird 这是一个将Android手机电量,短信,手机通知等数据发送给电脑端的项目,有兴趣可以自己打包部署使用一下,欢迎star

分类:
后端