antlr4 正则表达式生成器3解决排除字符处理

1、说明

对于 [^0-9] 正则要求式不能是字符0-9之间的数字,当前这种场景没有处理。

2、区间范围处理思路

可以使用排查法,总的字符选中范围,为0-0xffff 我们可以从中排除这个范围

即生成的字符范围为 [0 - '0'-1] 和 ['9'+1 - 0xffff] 的范围中随机选择。

为了简化处理过程,对于 [^0-9a-w] 我们就取这里的最小值,记为 min ,最大值记为 max

则最后的区间范围为 [0 - min-1] 和 [max+1 - 0xffff]。

3、代码的处理思路

1、分析

查看树的遍历过程 [^0-9a-w]

当遍历到 NegCharGroup 节点的时也就是匹配 ^ 字符时,我们要获取生成的随机字符的范围。 由于字符范围和其下面的子节点有关系,所以要使用变量来记住其下的子节点的遍历情况。 由于每个遍历函数都返回String,其参数都是固定类型,故不好传递参数,故定义全局变量。

2、解决办法

定义一个成员变量 List<String[]> recored 用来记录,后面的子节点的范围字符情况。

在访问到 NegCharGroup节点时,进行清除操作, 手动的访问其下面的子节点,在根据recored 来 生成最终的字符。

3、代码实现

ini 复制代码
/**
 * 用来记录,[^a-z]的范围情况
 */
private List<String[]> negRecords = new ArrayList<>();

visitCharClassExpr 函数调整

scss 复制代码
    // Character Class Expression
////    charClassExpr
////    : (NegCharGroup | NestedNegCharGroup | PosCharGroup | NestedPosCharGroup) charGroup EndCharGroup
//            ;
    @Override
    public String visitCharClassExpr(regexParser.CharClassExprContext ctx) {

        // 同时有 [^ 或者 [ 暂不支持
        if (ctx.NestedNegCharGroup() != null || ctx.NestedPosCharGroup() != null) {
            throw new RuntimeException("notSupportNestedCharGroup()");
        }
        // [^1-9]反字符的处理逻辑,
        if (ctx.NegCharGroup() != null) {
            // 清除集合
            negRecords.clear();
            // 手动调用,子集
            visitCharGroup(ctx.charGroup());
            return getNegDataChar();

        }
        return visitCharGroup(ctx.charGroup());
    }

    /**
     * 根据 negRecords记录来获取字符
     * @return
     */
    private String getNegDataChar() {
        List<Integer> nums = new ArrayList<>();
        for (String[] negRecord : negRecords) {
            for (int i = 0; i < negRecord.length; i++) {
                String data = negRecord[i];
                // 中文处理
                if (data.startsWith("\u")) {
                    String hexStr = data.replace("\u","");
                    nums.add(HexUtil.hexToInt(hexStr));
                } else {
                    // 普通字符,直接取ask码
                    nums.add((int) data.charAt(0));
                }
            }
        }
        // 获取最大值
        Integer max = nums.stream().max(Integer::compare).get();
        // 获取最小值
        Integer min = nums.stream().max(Integer::compare).get();
        // 对范围进行控制
        min = min<0?0:min;
        max = max>0xffff?0xffff:max;
        // 保存最随机生成的结果
        List<String> endDatas = new ArrayList<>();
        endDatas.add(randomChar(0,min));
        endDatas.add(randomChar(max+1,0xffff));
        // 随机选择一个结果进行返回
        return endDatas.get(R.nextInt(endDatas.size()));
    }

4、测试

csharp 复制代码
System.out.println(RegexUtil.gen("[^a-w0-9]{8}"));

打断点观察

值都顺利的添加进来了

运行结果如下:

css 复制代码
✱壸L醃胞I

5、完整代码

RegVistor2.java

java 复制代码
package com.regex;

import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import com.regex.g4.regexParser;
import com.regex.g4.regexParserBaseVisitor;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * 遍历整个正则的语法分析树
 */
public class RegVistor2 extends regexParserBaseVisitor<String> {

    // 随机字符串
    private final static Random R = new Random();

    // 是否包含特殊字符
    private final static Boolean MAYBE_SPECIAL_CHAR = Boolean.TRUE;


    /**
     * 用来记录,[^a-z]的范围情况
     */
    private List<String[]> negRecords = new ArrayList<>();

    //    root
    //    : regExp EOF
    //            ;
    @Override
    public String visitRoot(regexParser.RootContext ctx) {
        // 直接访问子节点
        return visitRegExp(ctx.regExp());
    }


    //    regExp
//    : branch (PIPE branch)*
//    ;
    @Override
    public String visitRegExp(regexParser.RegExpContext ctx) {

        // 或者任意一个分支的结果即可
        int index = R.nextInt(ctx.branch().size());
        // 获取其中一个分支的结果
        return visitBranch(ctx.branch(index));
    }

    //    branch
//    : piece*
//    ;
    @Override
    public String visitBranch(regexParser.BranchContext ctx) {
        StringBuilder stringBuilder = new StringBuilder();
        // 遍历piece 结果,即可或者所有的结果
        for (regexParser.PieceContext pieceContext : ctx.piece()) {
            stringBuilder.append(visitPiece(pieceContext));
        }
        return stringBuilder.toString();
    }

    //    piece
//    : atom quantifier?
//    ;
    @Override
    public String visitPiece(regexParser.PieceContext ctx) {
        StringBuilder sb = new StringBuilder();
        // 获取字符
        regexParser.AtomContext atom = ctx.atom();
        // quantifier 次数
        regexParser.QuantifierContext quantifier = ctx.quantifier();
        if (quantifier != null) {
            String num = visitQuantifier(quantifier);
            Integer regCount = Integer.valueOf(num);
            for (Integer i = 0; i < regCount; i++) {
                String data = visitAtom(atom);
                sb.append(data);
            }
            return sb.toString();
        }
        // 只有字符就返回生成的字符
        return visitAtom(atom);
    }



    //    atom
//    : Char
//    | charClass
//    | (LPAREN regExp RPAREN)
//    ;
    @Override
    public String visitAtom(regexParser.AtomContext ctx) {
        // 字符直接匹配
        if (ctx.Char() != null) {
            return ctx.Char().getText();
        }
        // 其他访问子分支
        if (ctx.charClass() != null) {
            return visitCharClass(ctx.charClass());
        }

        if (ctx.regExp() != null) {
            return visitRegExp(ctx.regExp());
        }

        return "";
    }

    //    charClass
//    : charClassEsc
//    | charClassExpr
//    | WildcardEsc
//    ;
    @Override
    public String visitCharClass(regexParser.CharClassContext ctx) {
        // 都是优先处理终结符  匹配. 返回一个任意字符
        if (ctx.WildcardEsc() != null) {
            // 返回一个任意字符,
            int[][] range = {{0x4e00, 0x9fa5}, {0x30, 0x39}, {0x61, 0x7a}, {0x41, 0x5a}};
            return getRandomChar(range);
        }
        // 访问其他子节点
        if (ctx.charClassEsc() != null) {
            return visitCharClassEsc(ctx.charClassEsc());
        }

        if (ctx.charClassExpr() != null) {
            return visitCharClassExpr(ctx.charClassExpr());
        }
        return "";
    }

    // 生成随机字符串
    private String getRandomChar(int[][] range) {

        String specialCharStr = "!@#$%^&*()_+|\\/?'"',,<>,.·~`";
        // 包含特殊字符
        if (RegVistor2.MAYBE_SPECIAL_CHAR && R.nextBoolean()) {
            return String.valueOf(specialCharStr.charAt(R.nextInt(specialCharStr.length())));
        } else {
            // 随机选择一组
            int[] subRange = range[R.nextInt(range.length)];
            char charValue = (char) (subRange[0] + R.nextInt(subRange[1] - subRange[0] + 1));
            return String.valueOf(charValue);
        }
    }


    //    charClassEsc
//    : SingleCharEsc
//    | NestedSingleCharEsc
//    | MultiCharEsc
//    | NestedMultiCharEsc
//    | catEsc
//    | complEsc
//    ;
    @Override
    public String visitCharClassEsc(regexParser.CharClassEscContext ctx) {

        // 单字符
        if (ctx.SingleCharEsc() != null) {
            negRecords.add(new String[]{ctx.SingleCharEsc().getText(),ctx.SingleCharEsc().getText()});
            return ctx.SingleCharEsc().getText();
        }
        // 逻辑单字符
        if (ctx.NestedSingleCharEsc() != null) {
            return getNestedSingleCharEsc(ctx.NestedSingleCharEsc());

        }
        // /d /w 等处理
        if (ctx.MultiCharEsc() != null) {
            return getMultiCharEscStr(ctx.MultiCharEsc().getText());
        }
        // /D 反逻辑处理
        if (ctx.NestedMultiCharEsc() != null) {
            return getMultiCharEscStr(ctx.NestedMultiCharEsc().getText());
        }
        // 访问子节点
        if (ctx.catEsc() != null || ctx.complEsc() != null) {
            throw new RuntimeException("不支持catEsc,和 complEsc");
        }
        return "";
    }

    /**
     * 获取反逻辑的字符串数据
     * @param terminalNode 反逻辑字符串节点数据集
     * @return
     */
    private String getNestedSingleCharEsc(TerminalNode terminalNode) {
        return null;
    }

    /**
     * 获取反逻辑的字符串数据
     * @param text
     * @return
     */



    /**
     * 根据字符,来或者返回的字符
     * @param text
     * @return
     */
    private String getMultiCharEscStr(String text) {

        List<String> rangeList = new ArrayList();
        switch (text) {
            // 空白字符
            case "\\s":
                rangeList.add(randomChar(' ', ' '));
                rangeList.add(randomChar('\t', '\t'));
                rangeList.add(randomChar('\r', '\r'));
                rangeList.add(randomChar('\n', '\n'));
                break;
            case "\\S":

                rangeList.add(randomChar(0x4e00, 0x9fa5));
                rangeList.add(randomChar(0x30, 0x39));
                rangeList.add(randomChar(0x61, 0x7a));
                rangeList.add(randomChar(0x41, 0x5a));

                break;

            case "\\i":
            case "\\c":
            case "\\C":
            case "\\I":
                throw new  RuntimeException("notSupportExp_i_I_c_C");
            case "\\d":

                rangeList.add(randomChar(0x30, 0x39));
                break;


            case "\\D":
                // \D:表示非数字
                rangeList.add(randomChar(0x4e00, 0x8fa5));
                rangeList.add(randomChar(0x61, 0x7a));
                rangeList.add(randomChar(0x41, 0x5a));
                break;


            case "\\w":
                // \w 表示匹配大小写英文字母、数字以及下划线,等价于'[a-za-z0-9_]'。
                rangeList.add(randomChar(0x61, 0x7a));
                rangeList.add(randomChar(0x41, 0x5a));
                rangeList.add(randomChar(0x30, 0x39));

                break;
            case "\\W":
                // \W :匹配任何非单词字符,等价于 [^A-Z a-z 0-9_]
                rangeList.add(randomChar(0x4e00, 0x9fa5));
                break;

        }

        return rangeList.get(R.nextInt(rangeList.size()));
    }


    // Character Class Expression
////    charClassExpr
////    : (NegCharGroup | NestedNegCharGroup | PosCharGroup | NestedPosCharGroup) charGroup EndCharGroup
//            ;
    @Override
    public String visitCharClassExpr(regexParser.CharClassExprContext ctx) {

        // 同时有 [^ 或者 [ 暂不支持
        if (ctx.NestedNegCharGroup() != null || ctx.NestedPosCharGroup() != null) {
            throw new RuntimeException("notSupportNestedCharGroup()");
        }
        // [^1-9]反字符的处理逻辑,
        if (ctx.NegCharGroup() != null) {
            // 清除集合
            negRecords.clear();
            // 手动调用,子集
            visitCharGroup(ctx.charGroup());
            return getNegDataChar();

        }
        return visitCharGroup(ctx.charGroup());
    }

    /**
     * 根据 negRecords记录来获取
     * @return
     */
    private String getNegDataChar() {
        List<Integer> nums = new ArrayList<>();
        for (String[] negRecord : negRecords) {
            for (int i = 0; i < negRecord.length; i++) {
                String data = negRecord[i];
                // 中文处理
                if (data.startsWith("\\u")) {
                    String hexStr = data.replace("\\u","");
                    nums.add(HexUtil.hexToInt(hexStr));
                } else {
                    // 普通字符,直接取ask码
                    nums.add((int) data.charAt(0));
                }
            }
        }
        // 获取最大值
        Integer max = nums.stream().max(Integer::compare).get();
        // 获取最小值
        Integer min = nums.stream().max(Integer::compare).get();
        // 对范围进行控制
        min = min<0?0:min;
        max = max>0xffff?0xffff:max;
        // 保存最随机生成的结果
        List<String> endDatas = new ArrayList<>();
        endDatas.add(randomChar(0,min));
        endDatas.add(randomChar(max+1,0xffff));
        // 随机选择一个结果进行返回
        return endDatas.get(R.nextInt(endDatas.size()));
    }


    //    charGroup
//    : posCharGroup? DASH DASH charClassExpr
//    | posCharGroup DASH charClassExpr
//    | posCharGroup DASH?
//    | DASH
//    | Chinese_Unicode* // 添加了中文编码处理
//    ;
    @Override
    public String visitCharGroup(regexParser.CharGroupContext ctx) {

        List<String> datas = new ArrayList<>();
        // 对中文字符进行处理
        if (ctx.Chinese_Unicode() != null && !ctx.Chinese_Unicode().isEmpty()) {
            // 获取unicode 编码
            for (TerminalNode terminalNode : ctx.Chinese_Unicode()) {
                String text = terminalNode.getText();
                // 生成中文,使用了hutool库
                String chinese = UnicodeUtil.toString(text);
                // 添加到选择列表中
                datas.add(chinese);
                // 添加到记录中
                negRecords.add(new String[]{text,text});

            }
            // 处理完,就返回
            return datas.get(R.nextInt(datas.size()));
        }

        // 获取一个列表
        datas.add(visitPosCharGroup(ctx.posCharGroup()));

        // 获取任意一个
        return datas.get(R.nextInt(datas.size()));
    }


    // Positive Character Group
//    posCharGroup
//    : DASH? (charRange | charClassEsc)+
//    ;
    @Override
    public String visitPosCharGroup(regexParser.PosCharGroupContext ctx) {
        List<String> datas = new ArrayList<>();
        if(ctx.DASH() != null) {
            datas.add("-");
            // 添加到记录中
            negRecords.add(new String[]{"-","-"});
        }
        // 添加到可选项中
        for (regexParser.CharClassEscContext charClassEscContext : ctx.charClassEsc()) {
            datas.add(visitCharClassEsc(charClassEscContext));
        }
        //  添加到可选项中
        for (regexParser.CharRangeContext charRangeContext : ctx.charRange()) {
            datas.add(visitCharRange(charRangeContext));
        }
        // 获取任意一个
        return datas.get(R.nextInt(datas.size()));
    }



//    charRange
//    : seRange
//    | XmlChar
//    ;

    @Override
    public String visitCharRange(regexParser.CharRangeContext ctx) {

        if (ctx.XmlChar() != null) {

            negRecords.add(new String[]{ctx.XmlChar().getText(),ctx.XmlChar().getText()});

            return ctx.XmlChar().getText();
        }
        // 接着访问子节点
        return super.visitCharRange(ctx);
    }


//    charOrEsc
//    : Chinese_Unicode
//    | XmlChar
//    | SingleCharEsc
//    ;
    @Override
    public String visitSeRange(regexParser.SeRangeContext ctx) {

        // [a-w] [0-9]
        String start = ctx.charOrEsc(0).getText();
        String end = ctx.charOrEsc(1).getText();
        // 添加进去
        negRecords.add(new String[]{start,end});

        // 判断为中文处理
        if (start.startsWith("\\u")) {
            // 获取unicode,转为 16进制字符串
            String hexStartStr = start.replace("\\u", "");
            String hexEndStr = end.replace("\\u","");
            // 16进制转为10进制
            int hexStart = HexUtil.hexToInt(hexStartStr);
            int hexEnd = HexUtil.hexToInt(hexEndStr);
             // 使用hutool库生成字符串
            char chinese = (char)RandomUtil.randomInt(hexStart, hexEnd + 1);
            return new String(new char[]{chinese});
        }
        // 返回随机数
        return randomChar(start.charAt(0),end.charAt(0));
    }


    //    quantifier
//    : QUESTION
//    | STAR
//    | PLUS
//    | StartQuantity quantity EndQuantity
//    ;
    @Override
    public String visitQuantifier(regexParser.QuantifierContext ctx) {

        // 匹配?,就返回随机值,0或者1
        if (ctx.QUESTION() != null) {
            return String.valueOf(R.nextInt(2));
        }
        // 匹配 * 号,返回 0-15 之间的数字
        if (ctx.STAR() != null) {
            return String.valueOf(R.nextInt(15));
        }
        // 匹配+  1到21 之间的整数,
        if (ctx.PLUS() != null) {
            return String.valueOf(R.nextInt(20) + 1);
        }
        // 有进行处理
        if (ctx.quantity() != null) {
            return visitQuantity(ctx.quantity());
        }
        return "0";
    }

    //    quantity
//    : quantRange
//    | quantMin
//    | QuantExact
//    ;
    @Override
    public String visitQuantity(regexParser.QuantityContext ctx) {
        // 这种情况要进行特殊处理下
        if (ctx.QuantExact() != null) {
            return ctx.QuantExact().getText();
        }

        if (ctx.quantMin() != null) {
            return visitQuantMin(ctx.quantMin());
        }

        if (ctx.quantRange() != null) {
            return visitQuantRange(ctx.quantRange());
        }
        return "0";
    }

//    quantRange
//    : QuantExact COMMA QuantExact
//    ;  {4,9} 等

    @Override
    public String visitQuantRange(regexParser.QuantRangeContext ctx) {
        TerminalNode terminalNodeMin = ctx.QuantExact(0);
        TerminalNode terminalNodeMax = ctx.QuantExact(1);

        int min = Integer.parseInt(terminalNodeMin.getText());

        int max = Integer.parseInt(terminalNodeMax.getText());

        if (min > max) {
            throw new RuntimeException("范围错误");
        }
        // 从可选范围中,选中一个
        return String.valueOf(min + R.nextInt(max - min + 1));
    }


    //    quantMin
//    : QuantExact COMMA
//            ; {8,}
    @Override
    public String visitQuantMin(regexParser.QuantMinContext ctx) {

        if (ctx.QuantExact() != null) {
            TerminalNode terminalNode = ctx.QuantExact();
            String text = terminalNode.getText();
            int min = Integer.parseInt(text);
            // 最小数 min - min+10
            return String.valueOf(R.nextInt(10) + min);
        }
        return "0";
    }

    /**
     * 生成随机字符串
     * @param start
     * @param end
     * @return
     */
    public String randomChar(int start,int end) {
        Random random = new Random();
        return String.valueOf((char) (start + random.nextInt(end - start + 1)));
    }

}
相关推荐
2401_8576100312 分钟前
Spring Boot框架:电商系统的技术优势
java·spring boot·后端
杨哥带你写代码2 小时前
网上商城系统:Spring Boot框架的实现
java·spring boot·后端
camellias_2 小时前
SpringBoot(二十一)SpringBoot自定义CURL请求类
java·spring boot·后端
背水3 小时前
初识Spring
java·后端·spring
晴天飛 雪3 小时前
Spring Boot MySQL 分库分表
spring boot·后端·mysql
weixin_537590453 小时前
《Spring boot从入门到实战》第七章习题答案
数据库·spring boot·后端
AskHarries3 小时前
Spring Cloud Gateway快速入门Demo
java·后端·spring cloud
Qi妙代码4 小时前
MyBatisPlus(Spring Boot版)的基本使用
java·spring boot·后端
宇宙超级勇猛无敌暴龙战神4 小时前
Springboot整合xxl-job
java·spring boot·后端·xxl-job·定时任务