一个简单高效的命令解析代码

命令解析其实应用到很多地方,常用的例如:

  1. 管理命令:GM(game manager),直播管理命令;一般是运营,管理人员,管理场景,管理房间等使用。
  2. 调试命令:例如实时查看进程信息,机器资源使用情况,打印详细日志等等。
  3. 权限命令:例如实时开通白名单/黑名单,给用户分配临时权限等等。
  4. 调度命令:人为实时更改函数调度,进程调度,机器调度等,流量分发、流量限制等,服务升降级等等。

作者云课堂:http://m.study.163.com/provider/480000001930416/index.htm?share=2&shareId=480000001930416

命令的应用场景,好处,无须多讲。下面说说这个命令解析的格式,大致跟mysql client 连接差不多:

mysql -h test.command.com -P 6379 -u root -p

当然,有一些更简单的一些处理方法,例如:直接根据空格来进行参数分割。比如下面这个游戏gm命令,增加类型为1的货币1000。

addmoney 1 1000

这样看起来很简单,很清晰。但是,如果操作这个命令,需要加上操作日志呢?

addmoney 1 1000  "重启 补偿"

这时候以空格来分割就有问题了。因为日志字符串中存在空格。当然有些东西,你可以通过妥协来解决。再例如,需要增加一个新命令,减少货币。

submoney 1 1000

其实从货币的角度,增加,减少本质只是对同种事物的不同操作,很多时候,我们设计代码的时候,就会设计成

money 1 1 1000    // 增加 类型1 1000
money 2 1 1000    // 减小 类型1 1000

当然,这仅仅是增加、减少;还有货币转换、货币清空、货币转移等等。如果用同一个命令money,根据这种格式,扩展性就会显得很无力。

所以,这里实现了开头所说的格式,虽然看起来相对并不太人性化。但是,兼容性,扩展性,是好于空格分割的。

接下来,上代码,核心入口函数,就是parseArgs。

#include <iostream>
#include <sstream>
 
using namespace std;
struct ArgInfo
{
    void clear()
    {
        sOpt.clear();
        sVal.clear();
    }
 
    // 值能为空,键不能为空
    inline bool empty()
    {
        return sOpt.empty();
    }
    string sOpt;
    string sVal;
};
 
enum
{
    IN_VALUE = 1,
    AFTER_VALUE,
};
 
#define LOG() cout
 
inline void skipSpace(const char *sArgs, size_t lArgsLen, size_t &i)
{
    for (; i < lArgsLen; ++i)
    {
        switch (sArgs[i])
        {
        case ' ':
        case '\t':
            break;
        default:
            return;
        }
    }
    return;
}
 
bool parseOpt(const char *sArgs, size_t lArgsLen, size_t &i)
{
    for (size_t lOld = i; i < lArgsLen; ++i)
    {
        switch (sArgs[i])
        {
        case ' ':
        case '\t':
        case '\n':
            if (i == lOld) return false;
            return true;
        case '\'':
        case '\"':
        case '-':
            return false;
        default:
            break;
        }
    }
    return true;
}
 
bool parseVal(const char *sArgs, size_t lArgsLen, size_t &i)
{
    for (size_t lOld = i; i < lArgsLen; ++i)
    {
        switch (sArgs[i])
        {
        case ' ':
        case '\t':
        case '\n':
            if (i == lOld) return false;
            return true;
        case '-':
            return false;
        default:
            break;
        }
    }
    return true;
}
 
bool parseQuotesVal(const char *sArgs, size_t lArgsLen, size_t &i)
{
    for (; i < lArgsLen; ++i)
    {
        switch (sArgs[i])
        {
        case '\"':
            return true;
        case '\\':
            ++i;
            break;
        default:
            break;
        }
    }
    return false;
}
 
bool parseArgs(const string &sArgs, vector<ArgInfo> &vArgs)
{
    if (sArgs.empty()) return true;
    ArgInfo tInfo;
 
    size_t i = 0;
    skipSpace(sArgs.c_str(), sArgs.size(), i);
    while (i < sArgs.size())
    {
        size_t lEnd = i;
        const char c = sArgs[i];
        if ( c == '-')
        {
            ++i;
            if (!parseOpt(sArgs.c_str(), sArgs.size(), ++lEnd))
            {
                return false;
            }
 
            if (!tInfo.empty())
            {
                vArgs.emplace_back(tInfo);
                tInfo.clear();
            }
            tInfo.sOpt = string(sArgs, i, lEnd - i);
        }
        else 
        {
            if (tInfo.sOpt.empty())
            {
                return false;
            }
            if (c == '\"')
            {
                ++i;
                if (!parseQuotesVal(sArgs.c_str(), sArgs.size(), ++lEnd))
                {
                    return false;
                }
            }
            else
            {
                if (!parseVal(sArgs.c_str(), sArgs.size(), ++lEnd))
                {
                    return false;
                }
            }
            tInfo.sVal = string(sArgs, i, lEnd - i);
            vArgs.emplace_back(tInfo);
            tInfo.clear();
        }
        skipSpace(sArgs.c_str(), sArgs.size(), ++lEnd);
        i = lEnd;
    }
    if (!tInfo.empty())
    {
        vArgs.emplace_back(tInfo);
    }
    return true;
}
 
void unitTest(const vector<string> &vTest)
{
    for (auto & sTest : vTest)
    {
        vector<ArgInfo> vRet;
        if (parseArgs(sTest, vRet))
        {
            ostringstream oss;
            oss << " " << sTest << " => ";
 
            for (auto & tRet : vRet)
            {
                oss << " " << tRet.sOpt << "#" << tRet.sVal;
            }
 
            LOG() << oss.str() << endl;
        }
        else
        {
            LOG() << " Format error:" << sTest << endl;
        }
    }
}
 
void testRightFormat()
{
    vector<string> vTest = {
        "",
        "-abc",
        "-a 123 -b abc",
        "-a -b 123abc",
        "-a 123abc -b",
        "-a \"12'ab\" -b \"cd 34\"",
        "-a -b \"123abc\"",
        "-a \"123abc\" -b",
        "-a \"123-abc\" -b",
        "-a \"\\\"123-abc\\\"\" -b"
    };
 
    unitTest(vTest);
}
 
void testWrongFormat()
{
    vector<string> vTest = {
        "a",
        "- a",
        "-a- 123 -b abc",
        "--a -b 123abc",
        "-\"a 123abc -b",
        "-\"a\" 123abc -b",
        "-a \"12'ab -b \"cd 34\"",
        "-a \"123abc\" -b ab cd",
        "-a \"123abc\" -b ab-cd",
        "-a 123 -b \"ab\"c"
    };
 
    unitTest(vTest);
}
 
int main()
{
    testRightFormat();
    //testWrongFormat();
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值