美文网首页
集合字符串解析[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