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)));
}
}