美文网首页
集合字符串解析[1,%2,100]

集合字符串解析[1,%2,100]

作者: small瓜瓜 | 来源:发表于2020-03-03 18:13 被阅读0次

    判断一个数是否在指定的集合内是很简单的,只需要将集合存入java的ListSet(根据集合的特性Set更适合)类型中,使用他的contains方法即可判断,但是很多时候集合并不固定,我们需要把他写到配置文件中去,这个时候该怎么办呢?

    比如:

    1. 有一个集合是1100的整数
    2. 有一个集合是1100的偶数
    3. 有一个集合是1100的偶数并且不要5060之间的偶数
    4. 有一个集合是1100的偶数并且里面的数也是3的倍数

    上面的两个问题我们应该怎么解决呢?怎么写到配置文件中呢?

    最简单的方法当然是之间枚举所有的数出来(但是这样太不优雅了,数值过大就不太好配置了)

    为了解决上面的问题,一个比较好的方式就是开发DSL了,使用antlr让开发更加简单

    贴代码了:

    # ^ 用来表示集合的交集运算
    # + 用来表示集合的与运算(可以省略)
    # - 用来表示集合的差运算
    # {} 改变( '^' > '+' = '-' )之间的运算优先级
    # [  代表 包含start
    # (  代表 不包含start
    # ]  代表 包含end
    # )  代表 不包含end
    # speed 代表步长,如果前面加个%代表求余为零的数
    
    grammar Test;
    
    range  : range op='^' range                             #Assoc
           | range op=('+'|'-') range                       #AddSub
           | range range                                    #Add
           | '{' range '}'                                  #Simple
           | ('['|'(') start ',' speed ',' end (')'|']')    #SSE
           | ('['|'(') start ',' end (')'|']')              #SE
           | ('['|'(') start (')'|']')                      #S
           ;
    
    start   :   INT;
    speed   :   '%'? INT;
    end     :   INT;
    
    
    SMALLBRACKETLEFT    :   '(';
    SMALLBRACKETRIGHT    :   ')';
    MEDIUMBRACKETLEFT   :   '[';
    MEDIUMBRACKETRIGHT   :   ']';
    ADDOPTION   :   '+';
    SUBOPTION   :   '-';
    
    INT         :   [0-9]+;
    NL          :   '\r'?'\n';
    WS          :   [ \t\r\n]+ -> skip;
    

    编写visitor:

    package cn.infomany.test0;
    
    import java.util.HashSet;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /**
     * @description: 范围访问器
     * @author: zhanjinbing
     * @data: 2020-03-03 15:17
     */
    public class RangesVisitor extends TestBaseVisitor<Object> {
    
        @Override
        public Object visitAdd(TestParser.AddContext ctx) {
            Set<Integer> result = new HashSet<>();
            Set<Integer> result0 = (Set<Integer>) visit(ctx.range(0));
            Set<Integer> result1 = (Set<Integer>) visit(ctx.range(1));
    
            result.addAll(result0);
            result.addAll(result1);
            return result;
        }
    
        @Override
        public Object visitSE(TestParser.SEContext ctx) {
            int speed = 1;
            int start = (int) visit(ctx.start());
            int end = (int) visit(ctx.end());
    
            return visitSSE(start, speed, end,
                    ctx.start.getType() == TestLexer.MEDIUMBRACKETLEFT,
                    ctx.stop.getType() == TestLexer.MEDIUMBRACKETRIGHT);
    
        }
    
        private Set<Integer> visitSSE(int start, int speed, int end, boolean hasStart, boolean hasEnd) {
            Set<Integer> result = new HashSet<>();
            for (int i = start; i < end; i += speed) {
                result.add(i);
            }
    
            if (!hasStart) {
                result.remove(start);
            }
    
            if (hasEnd) {
                result.add(end);
            }
    
            return result;
        }
    
        @Override
        public Object visitSimple(TestParser.SimpleContext ctx) {
            return visit(ctx.range());
        }
    
        @Override
        public Object visitS(TestParser.SContext ctx) {
            Set<Integer> results = new HashSet<>();
            if (ctx.start.getType() == TestLexer.SMALLBRACKETLEFT
                    || ctx.stop.getType() == TestLexer.SMALLBRACKETRIGHT) {
                return results;
            }
    
            results.add((Integer) visit(ctx.start()));
            return results;
        }
    
        @Override
        public Object visitSSE(TestParser.SSEContext ctx) {
            Object speedObj = visit(ctx.speed());
            int start = (int) visit(ctx.start());
            int end = (int) visit(ctx.end());
    
            Set<Integer> results;
    
            if (speedObj instanceof String) {
                results = visitSSE(start, 1, end,
                        ctx.start.getType() == TestLexer.MEDIUMBRACKETLEFT,
                        ctx.stop.getType() == TestLexer.MEDIUMBRACKETRIGHT);
    
                final Integer tmpInt = Integer.valueOf(((String) speedObj).substring(1));
    
                results = results.stream().filter(it -> it % tmpInt == 0).collect(Collectors.toSet());
    
            } else if (speedObj instanceof Integer) {
                results = visitSSE(start, (Integer) speedObj, end,
                        ctx.start.getType() == TestLexer.MEDIUMBRACKETLEFT,
                        ctx.stop.getType() == TestLexer.MEDIUMBRACKETRIGHT);
    
            } else {
                throw new RuntimeException("[speed]格式有误");
            }
            return results;
        }
    
        @Override
        public Object visitAddSub(TestParser.AddSubContext ctx) {
    
            Set<Integer> result = new HashSet<>();
    
            Set<Integer> result0 = (Set<Integer>) visit(ctx.range(0));
            Set<Integer> result1 = (Set<Integer>) visit(ctx.range(1));
    
            result.addAll(result0);
    
            if (ctx.op.getType() == TestLexer.ADDOPTION) {
                result.addAll(result1);
            } else {
                result.removeAll(result1);
            }
    
            return result;
        }
    
        @Override
        public Object visitAssoc(TestParser.AssocContext ctx) {
            Set<Integer> result = new HashSet<>();
    
            Set<Integer> result0 = (Set<Integer>) visit(ctx.range(0));
            Set<Integer> result1 = (Set<Integer>) visit(ctx.range(1));
    
            result0.stream().forEach(it -> {
                if (result1.contains(it)) {
                    result.add(it);
                }
            });
    
            return result;
        }
    
        @Override
        public Object visitStart(TestParser.StartContext ctx) {
            return Integer.valueOf(ctx.INT().getText());
        }
    
        @Override
        public Object visitSpeed(TestParser.SpeedContext ctx) {
            //  检查是不是单纯的int
            String reg = "\\d+";
            if (ctx.getText().matches(reg)) {
                return Integer.valueOf(ctx.INT().getText());
            }
            return ctx.getText();
        }
    
        @Override
        public Object visitEnd(TestParser.EndContext ctx) {
            return Integer.valueOf(ctx.INT().getText());
        }
    }
    

    主类:

    package cn.infomany.test0;
    
    import org.antlr.v4.runtime.CharStream;
    import org.antlr.v4.runtime.CharStreams;
    import org.antlr.v4.runtime.CommonTokenStream;
    import org.antlr.v4.runtime.tree.ParseTree;
    
    import java.util.ArrayList;
    
    /**
     * @description: 测试主程序
     * @author: zhanjinbing
     * @data: 2020-03-03 15:11
     */
    public class TestMain {
    
        public static void main(String[] args) throws Exception {
            // 新建一个CharStreams,从标准输入读取数据
            CharStream input = CharStreams.fromStream(System.in);
            // 新建一个词法符号的缓冲区,用于存储词法分析器将生成的词法符号
            TestLexer lexer = new TestLexer(input);
            // 新建一个词法符号的缓冲区,用于存储词法分析器将生成的词法符号
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            // 新建一个语法分析器,处理词法符号缓冲区中的内容
            TestParser parser = new TestParser(tokens);
    
            // 针对init规则,开始语法分析
            ParseTree tree = parser.range();
    
            RangesVisitor rangesVisitor = new RangesVisitor();
            Object visit = rangesVisitor.visit(tree);
            System.out.println("visit = " + visit);
        }
    }
    

    问题一:



    问题二:



    问题三:

    问题四:


    数组是乱序的,因为使用的Set来保存的
    引用文章请表明出处

    使用javafx开发了一个集合编写的工具,下面是截图:


    有高亮,动态生成,错误提示等功能。
    源代码位置https://gitee.com/MIEAPP/listGenerate

    相关文章

      网友评论

          本文标题:集合字符串解析[1,%2,100]

          本文链接:https://www.haomeiwen.com/subject/cnxklhtx.html