zoukankan      html  css  js  c++  java
  • 使用ANTLR进行命令行参数解析

    关于命令行参数的解析没有特定的规则,目前比较流行的有unix风格和微软风格。其实除了unix风格的比较一致外,微软自己提供的命令行参数解析就有很多种风格。在.net平台下的main函数中,仅仅把参数分解为以空格分割的数组,这对需要加开关,并且有的开关有自己的参数的情况是不够的,而且为了解析这些参数需要学习部分词法分析的知识,这对用处不是很大的命令行参数显得有些“鸡肋”,当然用Antlr来处理命令行参数更显得有些鸡肋,并且是大才小用,因为Antlr的语法规则比较复杂,学习起来有一定的难度。但对于已经使用Antlr进行DSL开发的开发人员来说,解决命令行参数解析的问题是举手之劳。

    我们需要先定义命令行参数的输入规则,参考.Net framework提供的命令行工具,制定如下的规则:

    1、以空格分割参数,与.net命令行参数保持一致。

    2、选项(option)或者开关(switch)用/ 或 \ 或 - 作为标志。

    3、选项可以有参数,使用冒号:分割,选项的参数如果是多个使用逗号(,)或者分号(;)分隔。

    4、选项之后是命令行自身的参数。

    5、选项名称以英文字母开头。

    6、选项的参数和命令行自身的参数可以使用英文字母和数字型、下划线,如果包含双字节码或者特殊字符,需要使用双引号。

    规则制定好以后,开始进入正题:

    第一步,需要从Antlr网站(http://www.antlr.org)上下载Antlr类库及相关的学习资料及工具,工具中比较重要的是Antlr works。

    第二步,使用Antlr works或者文本编辑器编辑词法和语法规则:

    grammar CmdPara;
    options{
        language=CSharp2;
        output=AST;
        ASTLabelType=CommonTree;
    }

    tokens{
        Option;
    }

    cmdLine    :    String option* para* EOF!;

    option
        :    swt ((':' para)((','|';')para)*)? ->^(Option swt para*);

    swt    :    ('/'|'-'|'\\') ID -> ID;

    para    :    String|INT|ID;

    ID    :    ('a'..'z'|'A'..'Z')(('a'..'z')
            |('A'..'Z')
            |'0'..'9'
            |'&'
            |'/'
            |'\\'
            |'.'
            |'_'
            )*; //don't support chinese

    String    :    ('"' .+ '"')|('\'' .+ '\'');

    INT    :    ('1'..'9')('0'..'9')*
        ;

    WS   
        :  (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;}
        ;

    第三步:定义命令行参数解析实体类

    namespace Rz.CmdLine
    {

        //命令行对象
        public class CmdLine {

            //可执行文件的文件名

            public string ExeFile { get; set; }

            private List<Switch> _cmdSwitchs = new List<Switch>();

            //开关对象列表

            public List<Switch> CmdSwitchs
            {
                get { return _cmdSwitchs; }
            }

            private List<string> _parameters = new List<string>();

            //命令行参数

            public List<string> Parameters
            {
                get { return _parameters; }
            }

        }

        //开关实体类

        public class Switch {

            //开关名称

            public string SwitchName { get; set; }

           //开关参数

            private List<string> _parameters = new List<string>();

            public List<string> Parameters
            {
                get { return _parameters; }
            }
        }
    }

     

    第四步:使用Antlr定义语法树遍历器

    tree grammar CmdWalker;
    options{
        language=CSharp2;
        tokenVocab=CmdPara;
            ASTLabelType=CommonTree;
    }
    @header{
    using Rz.CmdLine;
    }
    @members{

    }
    cmdLine    returns[CmdLine r = new CmdLine()]
        :    String
        {
            r.ExeFile=$String.Text;
        }
        (option{r.CmdSwitchs.Add($option.r);})*
        (para{r.Parameters.Add($para.r);})*
        ;

    option    returns[Switch r = new Switch()]
        :   
        ^(Option
        swt{r.SwitchName=$swt.r;}
        (para{r.Parameters.Add($para.r);})*
        )
        ;

    swt    returns[string r]
        :    ID{r=$ID.Text;}
        ;

    para    returns[string r]
        :   
        String{r=$String.Text;}
        |INT{r=$INT.Text;}
        |ID{r=$ID.Text;}
        ;

    第 五 步:定义解析命令行的静态类,把命令行转变为前面定义的实体类。

        public class CmdEngine
        {

            public static CmdLine Parse(string cmdLine) {

                ANTLRStringStream input = new ANTLRStringStream(cmdLine);

                CmdParaLexer lex = new CmdParaLexer(input);

                CommonTokenStream token = new CommonTokenStream(lex);

                CmdParaParser pars = new CmdParaParser(token);

                CommonTreeNodeStream tree = new CommonTreeNodeStream(pars.cmdLine().Tree);

                CmdWalker walker = new CmdWalker(tree);

                return walker.cmdLine();

            }

        }

    第六步:编写测试程序

    static void Main(string[] args)
    {

        Console.WriteLine(string.Join(" ",args));

        CmdLine cmd = CmdEngine.Parse(Environment.CommandLine);

        Console.WriteLine(cmd.ExeFile);

        foreach (Switch s in cmd.CmdSwitchs) {
            Console.WriteLine("Switch /{0}:{1}",s.SwitchName,string.Join(",",s.Parameters.ToArray()));
        }

        foreach (string s in cmd.Parameters) {
            Console.WriteLine("Command Parameter [{0}]:{1}",cmd.Parameters.IndexOf(s),s);
        }

        Console.ReadLine();
    }

    第七步:查看程序运行结果

    image

    源代码稍后会放到google开源库中,由于codeplex的速度太慢,实在无法忍受,希望有一天微软在Internet上跟上google的脚步。

    参考:

    CodePlex上一个开源参数解析类库

    以-标明选项

    短选项,-sHello,其中-是标志位,s是选项,Hello是参数。或者-s Hello,这个差别在于选项与参数之间使用空格分割。

    多个短选项:-x -y -z -w 或者-xyzw 或者-xy -zw

    长选项:--long Hello 或者--long=hello

    一个开源的Unix风格的参数解析


  • 相关阅读:
    binder机制理解
    Android 资源目录
    Andriod 构建项目流程
    dpi、ppi 、dp、sp、px、 pt相关概念
    短语、直接短语和句柄
    MySql优化
    java虚拟机内存管理
    redis
    linux——nginx的安装及配置
    linux——高级文本处理命令之wc、cut、sort
  • 原文地址:https://www.cnblogs.com/77543/p/1424238.html
Copyright © 2011-2022 走看看