Index.html
<html>
<head>
<title>正则表达式图形化工具</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<script type="text/javascript" src="js/jquery.js"></script>
</head>
<body>
<style type="text/css"> .animation-Box{ width: 600px; height: 400px; } .info-Box{ width: 600px; height: 400px; overflow: scroll; } .flow-chart{ text-align: center; } .flow-chart ul{ } .flow-chart ul li{ margin-top: 5px; border: 1px solid red; padding: 5px; margin: 5px; } .flow-chart ul li .reg{ background-color: #DB3434; color: #fff; padding: 5px 80px; font-size: 18px; } .flow-chart ul li .res{ background-color: #146E1F; color: #fff; padding: 2px 80px; font-size: 18px; height: 18px; line-height: 18px; } .content{ display: inline-block; vertical-align: middle; } .branch{ display: inline-block; vertical-align: middle; } #source{ width:800px; margin:20px auto 0; text-align:center; margin-bottom: 5px; } #source ul li{ margin-top: 5px; } #regexpInput,#textInput{ width: 500px; height: 30px; border: 1px solid gray; padding: 0 5px; } #beginBtn{ padding: 5px 15px; } text{ font-size: 20px; } </style>
<div id="source">
<ul >
<li>正则表达式:<input type="text" id="regexpInput"/></li>
<li><p style="color:red;">请填入正则表达式,支持格式:<br />1,没有斜线,如:<span style="font-size:20px;">\d|\s</span> ; <br />
2,有斜线,如:<span style="font-size:20px;">/\d|\s/</span></p></li>
<li><p style="color:red;">点击展示按钮,将会在下方生成正则流程图;同时在控制台中将会输出该正则的数据结构。</p></li>
<li><p style="color:red;">请在高级浏览器中运行。</p></li>
<li style="display:none;">需要匹配的字符串:<input type="text" id="textInput"/></li>
<li><input type="button" id="beginBtn" value="展示流程图" /></li>
</ul>
</div>
<style type="text/css"> path{ stroke:#000000; fill:none; stroke-width: 2px } rect{ fill:#bada55; } text{ fill:#000; } tspan{ } </style>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100%" width="100%" id="flowChart">
<g></g>
</svg>
<div style="height:20px;position:fixed;top:0;left:0;text-algin:center;font-size:20px;font-weight:bold;background-color:white;" id="regFixed"></div>
</body>
<script type="text/javascript" src="js/require.js"></script>
<script src="js/d3.v3.min.js" charset="utf-8"></script>
<script src="js/dagre-d3.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript"> require.config({ baseUrl: "js/" }); var regexpInputDom=document.getElementById('regexpInput'); var textInputDom=document.getElementById('textInput'); var beginBtnDom=document.getElementById('beginBtn'); beginBtnDom.onclick=function(){ require(['main'],function(main){ main.init(textInputDom.value,regexpInputDom.value); }); } $(window).scroll(function(){ if($(this).scrollTop()>40&&$('#regexpInput').val()!==''){ $('#regFixed').html($('#regexpInput').val()).show(); }else{ $('#regFixed').hide(); } }); </script>
</html>
common.css
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{padding:0;margin:0}
address,caption,cite,code,dfn,em,var{font-style:normal;font-weight:normal}
pre,kbd,samp,tt{font-family:monospace;line-height:100%}
q:before,q:after{content:''}
abbr,acronym{border:0;font-variant:normal}
sup{vertical-align:text-top}
sub{vertical-align:text-bottom}
ul,li{list-style:none}
em,.em{font-style:italic}
strong,.strong{font-weight:bold}
img{border:0;vertical-align:top;-ms-interpolation-mode:bicubic}
table{border-collapse:collapse;border-spacing:0}
caption,th{text-align:left}
fieldset{border:none}
legend{display:none}
textarea{resize:none}
button{overflow:visible}
label,button{cursor:pointer;_cursor:hand}
input,select,textarea{font:normal normal 14px "Verdana";color:#333;outline:none;}
textarea,input[type|="text"],input[type|="password"]{-webkit-appearance:none}
abbr[title],acronym[title]{border-bottom:1px dotted;cursor:help}
input[type="hidden"]{display:none!important}
h1,.h1{font-size:18px;font-weight:bold}
h2,.h2{font-size:16px;font-weight:bold}
h3,.h3{font-size:14px;font-weight:bold}
h4,.h4{font-size:14px;font-weight:normal}
h5,.h5{font-size:12px;font-weight:normal}
h6,.h6{font-size:12px;font-weight:normal}
.f12px{font-size:12px}
.f14px{font-size:14px}
.fl{float:left}
.fr{float:right}
a{color:#333;text-decoration:none;outline:none}
a:hover{text-decoration:underline;outline:none}
.blod a,.blod{font-weight:bold}
.center{text-align:center}
.mid{vertical-align:middle}
.hide{display:none}
.submit{cursor:pointer;border:0}
.collr,.collr .colr{overflow:hidden;zoom:1}
.collr .coll{float:left;_margin-right:-3px}
.collr .coll img{vertical-align:top}
.p-collr{position:relative;zoom:1}
.p-collr .coll{position:absolute;left:0;top:0}
.p-collr .colr{zoom:1}
.clear{clear:both;font-size:0px;width:0px;height:0;visibility:hidden;line-height:0}
.clearfix{*zoom:1}
.clearfix:after{content:"";display:block;height:0;clear:both;visibility:hidden}
body{background-color:#FFF;font:12px/22px Arial,Tahoma,Verdana,"\5B8B\4F53";color:#333}
main.js
define('main',['draw','Meta','NFA'],function(draw,Meta,NFA) {
function init(inputStr,regexpStr) {
if (regexpStr === '') {
return;
} else if (/^\//.test(regexpStr) && /(?:\/|\/i|\/g|\/m)$/.test(regexpStr)) {
regexpStr = regexpStr.replace(/^\//,'').replace(/(\/|\/i|\/g|\/m)$/,'');
}
try {
var reg = new RegExp(regexpStr);
} catch (e) {
alert(e);
}
var Metas = [];
Meta(regexpStr,Metas,1);
console.log('该正则表达式的生成数据结构如下:');
console.log(Metas);
var obj = document.getElementById('flowChart');
draw.drawModel(Metas,obj);
}
return {
init: init
}
});
draw.js
define('draw',['description'],function(des) {
var obj = {
drawModel: function(arr) {
var g = new dagreD3.graphlib.Graph().setGraph({});
g.setGraph({
nodesep: 70,ranksep: 50,rankdir: "LR",marginx: 20,marginy: 20
});
var preNodes = [];
var depth = [];
var topIndex = 0;
function drawBranch(branchArr,curPreNodes,type,operator) {
depth.push(1);
var curNodeName = '';
var bPreNodes = [];
var len = depth.length;
if (type) {
preNodes = curPreNodes;
curNodeName = 'node' + topIndex + depth.length + 't' + 's' + Math.random();
g.setNode(curNodeName,{
label: '进入' + type
});
if (type === '捕获分组') {
g.node(curNodeName).style = "fill:#3B639F";
} else if (type === '非捕获分组') {
g.node(curNodeName).style = "fill:#f5f8fc";
} else if (type === '肯定环视') {
g.node(curNodeName).style = "fill:#10c2ce";
} else if (type === '否定环视') {
g.node(curNodeName).style = "fill:#BBFFFF";
}
preNodes.forEach(function(nodeName) {
g.setEdge(nodeName,curNodeName,{
label: ''
});
});
curPreNodes = [curNodeName];
preNodes = curPreNodes;
}
for (var i = 0,len = branchArr.length; i < len; i++) {
if (len !== 1) {
preNodes = curPreNodes;
curNodeName = 'node' + topIndex + depth.length + i + 's' + Math.random();
g.setNode(curNodeName,{
label: '进入' + '分支'
});
g.node(curNodeName).style = "fill:#aa73d1";
preNodes.forEach(function(nodeName) {
g.setEdge(nodeName,{
label: ''
});
});
preNodes = [curNodeName];
}
for (var j = 0,c = branchArr[i].length; j < c; j++) {
if (depth.length === 1) {
topIndex = '' + i + j;
}
if (branchArr[i][j].branch.length === 0) {
curNodeName = 'node' + topIndex + depth.length + i + j + Math.random();
g.setNode(curNodeName,{
label: des(branchArr[i][j].atom,j) + ' 匹配' + des(branchArr[i][j].operator,j,true)
});
preNodes.forEach(function(nodeName) {
g.setEdge(nodeName,{
label: ''
});
});
preNodes = [curNodeName];
} else {
preNodes = drawBranch(branchArr[i][j].branch,preNodes,branchArr[i][j].type,branchArr[i][j].operator);
}
}
if (len !== 1) {
curNodeName = 'node' + topIndex + depth.length + i + 'e' + Math.random();
g.setNode(curNodeName,{
label: ('分支' + '结束') + (operator ? ' 匹配' + des(operator,undefined,true) : '')
});
g.node(curNodeName).style = "fill:#aa73d1";
preNodes.forEach(function(nodeName) {
g.setEdge(nodeName,{
label: ''
});
});
preNodes = [curNodeName];
}
bPreNodes.push(preNodes);
}
if (type) {
curNodeName = 'node' + topIndex + depth.length + 't' + 'e' + Math.random();
g.setNode(curNodeName,{
label: (type + '结束') + (operator ? ' 匹配' + des(operator,true) : '')
});
if (type === '捕获分组') {
g.node(curNodeName).style = "fill:#3B639F";
} else if (type === '非捕获分组') {
g.node(curNodeName).style = "fill:#f5f8fc";
} else if (type === '肯定环视') {
g.node(curNodeName).style = "fill:#10c2ce";
} else if (type === '否定环视') {
g.node(curNodeName).style = "fill:#BBFFFF";
}
bPreNodes.forEach(function(nodeName) {
g.setEdge(nodeName,{
label: ''
});
});
preNodes = [curNodeName];
bPreNodes = preNodes;
}
depth.pop();
return bPreNodes;
}
g.setNode('start',{
label: 'start',title:'ssss'
});
preNodes.push('start');
g.node('start').style = "fill: #999";
preNodes = drawBranch(arr,preNodes);
g.setNode('end',{
label: 'end'
});
g.node('end').style = "fill: #999";
preNodes.forEach(function(nodeName) {
g.setEdge(nodeName,'end',{
label: ''
});
});
g.nodes().forEach(function(v) {
var node = g.node(v);
node.rx = node.ry = 5;
});
var svg = d3.select("svg"),inner = svg.select("g");
var render = new dagreD3.render();
render(inner,g);
var zoom = d3.behavior.zoom().on("zoom",function() {
inner.attr("transform","scale(" + d3.event.scale + ")");
});
svg.call(zoom);
var initialScale = Math.max(0.6,$(window).width() / g.graph().width);
zoom
.scale(initialScale)
.event(svg);
svg.attr('width',g.graph().width * initialScale);
d3.selectAll('g').data([1,2,3,4,5,6,7]).enter().append('text').text(function(d){return d;});
}
};
return obj;
});
define('Meta',function() {
var operatorsReg = /^(?:\+\?|\*\?|\?\?|\+|\*|\?|\{\d+,\d*\}\??|\{\d+,?\}\??)/;
function findOperator(str) {
var oResult = operatorsReg.exec(str);
if (oResult === null) {
return {
operator: '',length: 0
}
} else {
return {
operator: oResult[0],length: oResult[0].length
}
}
}
function readGroup(str) {
var r = /[(|)]|\(\?\:/g,res,leftGroup = [],rightGroup = [],orGroup = [];
var tempStr = str.replace(/\\./g,'&&');
while (res = r.exec(str)) {
if (res[0] === '(' || res[0] === '(?:') {
leftGroup.push(r.lastIndex - 1);
} else if (res[0] === '|') {
orGroup.push(r.lastIndex - 1);
} else if (res[0] === ')') {
rightGroup.push(r.lastIndex - 1);
}
}
var paraArr = [],tempParaCL = 0,tempParaCR = 0,paraL = 0,paraR = 0;
for (var k = 0,b = 0,c = tempStr.length; k < c; k++) {
tempParaCL = 0,paraR = 0;
if (tempStr.charAt(k) === '(') {
paraL = k;
for (b = k + 1; b < c; b++) {
if (tempStr.charAt(b) === ')') {
tempParaCR++;
if (tempParaCR > tempParaCL) {
paraR = b;
paraArr.push({
l: paraL,r: paraR
});
break;
}
} else if (tempStr.charAt(b) === '(') {
tempParaCL++;
}
}
}
}
var resArr = [];
if (leftGroup.length === 0) {
if (orGroup.length) {
for (var j = 0,l = orGroup.length; j < l; j++) {
if (j === 0) {
resArr.push({
l: 0,r: orGroup[j] - 1,or: orGroup[j]
});
} else {
resArr.push({
l: orGroup[j - 1] + 1,or: orGroup[j]
});
}
}
} else {
resArr = [];
}
} else {
if (orGroup.length) {
var tempCountL = 0,tempCountR = 0,realL = 0,realR = 0;
for (var j = 0,l = orGroup.length; j < l; j++) {
tempCountL = 0,realR = 0;
for (var n = orGroup[j]; n > 0; n--) {
if (tempStr[n] === ')') {
tempCountR++;
} else if (tempStr[n] === '(') {
tempCountL++;
if (tempCountL > tempCountR) {
realL = n;
break;
}
}
}
tempCountL = 0,tempCountR = 0;
for (n = orGroup[j]; n < tempStr.length; n++) {
if (tempStr[n] === '(') {
tempCountL++;
} else if (tempStr[n] === ')') {
tempCountR++;
if (tempCountR > tempCountL) {
realR = n;
break;
}
}
}
resArr.push({
l: realL,r: realR,or: orGroup[j]
});
}
} else {
resArr = [];
}
}
return {
leftGroup: leftGroup,rightGroup: rightGroup,orGroup: orGroup,resArr: resArr,paraArr: paraArr
}
}
function getRightPara(leftPara,paraArr) {
for (var i = 0,len = paraArr.length; i < len; i++) {
if (paraArr[i].l === leftPara) {
return paraArr[i].r;
}
}
}
var groupIndex = 1;
function parseMeta(str,arr,isInit) {
if (isInit) {
groupIndex = 1;
}
var groupObj = readGroup(str);
var atIndex = 0,atChar = '',tempAtom = '',isUnicode = false,isHex = false,paraRight = 0,finaIndex = 0;
for (var len = str.length; atIndex < len; atIndex++) {
isUnicode = false;
isHex = false;
atChar = str.charAt(atIndex);
if (arr[finaIndex] === undefined) {
arr[finaIndex] = [];
}
arr[finaIndex][arr[finaIndex].length] = {};
if (atChar === '\\') {
subStr = str.substring(atIndex + 1);
if (/^u[0-9a-fA-F]{4}/.test(subStr)) {
tempAtom = '\\\\' + /^u[0-9a-fA-F]{4}/.exec(subStr)[0];
isUnicode = true;
atIndex += 5;
} else {
isUnicode = false;
}
if (/^x[0-9a-fA-F]{2}/.test(subStr)) {
tempAtom = '\\\\' + /^x[0-9a-fA-F]{2}/.exec(subStr);
atIndex += 3;
isHex = true;
} else {
isHex = false;
}
if (!isUnicode && !isHex) {
tempFun = makeMeta(atChar);
atIndex++;
tempAtom = tempFun(str.charAt(atIndex));
}
arr[finaIndex][arr[finaIndex].length - 1].index = atIndex;
arr[finaIndex][arr[finaIndex].length - 1].atom = tempAtom;
operatorObj = findOperator(str.substring(atIndex + 1));
arr[finaIndex][arr[finaIndex].length - 1].operator = operatorObj.operator;
atIndex += operatorObj.length;
arr[finaIndex][arr[finaIndex].length - 1].branch = [];
arr[finaIndex][arr[finaIndex].length - 1].length = 0;
} else if (atChar === '[') {
arr[finaIndex][arr[finaIndex].length - 1].index = atIndex;
subStr = str.substring(atIndex);
tempFun = makeMeta('');
tempAtom = tempFun((/\[.*?(?=([^\\])(\]))/.exec(subStr)[0]) + (/\[.*?(?=([^\\])(\]))/.exec(subStr)[1]) + (/\[.*?(?=([^\\])(\]))/.exec(subStr)[2]));
atIndex += tempAtom.length;
arr[finaIndex][arr[finaIndex].length - 1].atom = tempAtom;
operatorObj = findOperator(str.substring(atIndex));
arr[finaIndex][arr[finaIndex].length - 1].operator = operatorObj.operator;
atIndex += operatorObj.length - 1;
arr[finaIndex][arr[finaIndex].length - 1].branch = [];
arr[finaIndex][arr[finaIndex].length - 1].length = 0;
} else if (atChar === '|') {
arr[finaIndex].pop();
finaIndex++;
if (atIndex === 0) {
arr[finaIndex - 1] = [];
arr[finaIndex - 1][0] = {};
arr[finaIndex - 1][0].index = atIndex;
tempAtom = '';
arr[finaIndex - 1][0].atom = tempAtom;
arr[finaIndex - 1][0].operator = '';
arr[finaIndex - 1][0].branch = [];
arr[finaIndex - 1][0].length = 0;
}
if (atIndex === str.length - 1 || str[atIndex + 1] === '|') {
arr[finaIndex] = [];
arr[finaIndex][0] = {};
arr[finaIndex][0].index = atIndex;
tempAtom = '';
arr[finaIndex][0].atom = tempAtom;
arr[finaIndex][0].operator = '';
arr[finaIndex][0].branch = [];
arr[finaIndex][0].length = 0;
}
} else if (atChar === '(') {
paraRight = getRightPara(atIndex,groupObj.paraArr);
arr[finaIndex][arr[finaIndex].length - 1].index = atIndex;
subStr = str.substring(atIndex + 1,paraRight);
if (/^\?:/.test(subStr)) {
atIndex += 2;
subStr = /^\?:(.*)$/.exec(subStr)[1];
arr[finaIndex][arr[finaIndex].length - 1].type = '非捕获分组';
} else if (/^\?=/.test(subStr)) {
atIndex += 2;
subStr = /^\?=(.*)$/.exec(subStr)[1];
arr[finaIndex][arr[finaIndex].length - 1].type = '肯定环视';
} else if (/^\?!/.test(subStr)) {
atIndex += 2;
subStr = /^\?!(.*)$/.exec(subStr)[1];
arr[finaIndex][arr[finaIndex].length - 1].type = '否定环视';
} else {
arr[finaIndex][arr[finaIndex].length - 1].type = '捕获分组' + groupIndex++;
}
atIndex += subStr.length + 1;
arr[finaIndex][arr[finaIndex].length - 1].atom = subStr;
operatorObj = findOperator(str.substring(atIndex + 1));
arr[finaIndex][arr[finaIndex].length - 1].operator = operatorObj.operator;
atIndex += operatorObj.length;
arr[finaIndex][arr[finaIndex].length - 1].branch = [];
parseMeta(subStr,arr[finaIndex][arr[finaIndex].length - 1].branch);
arr[finaIndex][arr[finaIndex].length - 1].length = 0;
} else {
arr[finaIndex][arr[finaIndex].length - 1].index = atIndex;
tempAtom = str.charAt(atIndex);
arr[finaIndex][arr[finaIndex].length - 1].atom = tempAtom;
operatorObj = findOperator(str.substring(atIndex + 1));
arr[finaIndex][arr[finaIndex].length - 1].operator = operatorObj.operator;
atIndex += operatorObj.length;
arr[finaIndex][arr[finaIndex].length - 1].branch = [];
arr[finaIndex][arr[finaIndex].length - 1].length = 0;
}
}
}
function makeMeta(firstChar) {
var Meta = firstChar;
return function(atChar) {
Meta += atChar;
return Meta;
}
}
return parseMeta;
});
运行结果如图: