正则表达式可视化工具

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,th,var{font-style:normal;font-weight:normal}
pre,code,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==*/
table{border-collapse:collapse;border-spacing:0}
caption,th{text-align:left}

/*==Form==*/
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;/*去除Opera浏览器这些标签的默认样式*/}
textarea,input[type|="text"],input[type|="password"]{-webkit-appearance:none/*去除webkit浏览器这些标签的默认样式*/}
abbr[title],
acronym[title]{border-bottom:1px dotted;cursor:help}
input[type="hidden"]{display:none!important}

/*==h1~h6==*/
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,.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{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);
        //NFA(inputStr, meta, showMatch);
    }
    return {
        init: init
    }
});

draw.js

define('draw', ['description'], function(des) {

    /*var getBreadth=function(arr){
        var breadth=arr.length;
        var tempBreadth=0;
        for(var i=0,len=arr.length;i<len;i++){
            tempBreadth=0;

        }
    };*/
    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;
            // node命名规则 'node'+顶级index+深度+分支号+名称+随机数
            function drawBranch(branchArr, curPreNodes, type, operator) {
                //type = type ? type : '分支';
                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: 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, curNodeName, {
                                //label: curNodeName
                                label: ''
                            });
                        });
                        preNodes = [curNodeName];
                    }


                    for (var j = 0, c = branchArr[i].length; j < c; j++) {
                        // 顶级Index
                        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, curNodeName, {
                                    //label: curNodeName
                                    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, curNodeName, {
                                //label: curNodeName
                                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, undefined, 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, curNodeName, {
                            //label: curNodeName
                            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);
            preNodes = drawBranch(arr, preNodes);
            g.setNode('end', {
                label: 'end'
            });
            g.node('end').style = "fill: #999";
            preNodes.forEach(function(nodeName) {
                g.setEdge(nodeName, 'end', {
                    //label: 'end'
                    label: ''
                });
            });
            // Set some general styles
            g.nodes().forEach(function(v) {
                var node = g.node(v);
                node.rx = node.ry = 5;
            });

            var svg = d3.select("svg"),
                inner = svg.select("g");

            // Create the renderer
            var render = new dagreD3.render();
            // Run the renderer. This is what draws the final graph.
            render(inner, g);
            // Center the graph
            //var initialScale = 0.75;
            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('height', g.graph().height * initialScale + 40);
            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;
});

meta.js

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,
            tempParaCR = 0,
            paraL = 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,
                            r: orGroup[j] - 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,
                    tempCountR = 0,
                    realL = 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);
            //finaIndex=arr.length;
            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)) { // unicode转义
                    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)) { // 16进制转义
                    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;
});

运行结果如图:

这里写图片描述


这里写图片描述


这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值