需要更简单的方法来替换嵌套的自定义标记与HTML等效

时间:2012-12-20 19:02:47

标签: javascript regex

我正在寻找一种方法来正确地用他们的HTML等价替换嵌套的自定义标签。例如,假设我们有以下文本:

This is {b:bold text}

应该成为:

This is <b>bold text</b>

我知道我可以 - 并且可能应该 - 使用像“大胆”类的跨度而不是旧的“b”标签,但是有一个原因我坚持使用“b”来达到目的这个例子。我可能有嵌套标签:

This is {b:bold text and {i:italic}}

应该成为:

This is <b>bold text and <i>italic</i></b>

但是,我有这些正则表达式(我使用replace()函数):

/\{b:([\s\S]*?)\}/gm
/\{i:([\s\S]*?)\}/gm

然后结果将成为:

This is <b>bold text and <i>italic</b></i>

标签没有正确嵌套。同样,在这种情况下,使用跨度可以解决这个问题,但这不适用于“ul”,“li”,“h1”等... 如果文本如下所示,贪婪的正则表达式也会引起问题:

This is {b:bold text} and {i:italic}

所以我当前的解决方案是首先用span替换所有内容,使用data-tag属性指示它实际应该是什么,然后使用jQuery查找所有span节点并用适当的标记替换它们:

{h:This is a header}
becomes
<span data-tag='h1'>This is a header</span>
becomes
<h1>This is a header</h1>

它运作良好,但我想知道是否有更简单的方法来做到这一点。中间方法感觉有点像胶带解决方案,我想“美化”它。

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

简单的堆栈有很长的路要走

function doReplace( str ) {
    var stack = [],
        ret = [],
        ch;
    for( var i = 0, l = str.length; i < l; ++i ) {
        var ch = str.charAt(i);

        if( ch === "{" ) {
            var pos = str.indexOf( ":", i+1);
            if( pos < 0 ) {
                throw new SyntaxError();
            }
            var tagName = str.substring( i + 1, pos);
            if( /[^A-Za-z0-9]/.test(tagName)) {
                throw new SyntaxError();
            }
            ret.push( "<" + tagName + ">" );
            stack.push( tagName);
            i+= tagName.length + 1;
        }
        else if( ch === "}" ) {
            if( !stack.length ) {
                throw new SyntaxError();
            }
            var tagName = stack.pop();
            ret.push( "</" + tagName + ">" );
        }
        else {
            ret.push( ch );
        }
    }

    if( stack.length ) {
        throw new SyntaxError();
    }

    return ret.join("");
}

doReplace( "This is {b:bold text {i:italic text{i:italic text{i:italic text{i:italic text{i:italic text{i:italic text}}}}}}}")
//"This is <b>bold text <i>italic text<i>italic text<i>italic text<i>italic text<i>italic text<i>italic text</i></i></i></i></i></i></b>"

答案 1 :(得分:0)

抛弃正则表达式并使用其中一种模板工具,例如Mustachet.js

编辑: 已修复!

...但如果你在生活中避免这种简单,那么就是如何用正则表达式来做到这一点:

var rex = /\{(b|i):([^{}]*)\}/igm,
    parse = function(txt) {
        var cnt = 0,
            more = true;
        while (more) {
            txt = txt.replace(rex, function(match, $1, $2) {
                return "<" + $1 + ">" + $2 + "</" + $1 + ">";
            });
            more = rex.test(txt);
            cnt++;
        }
        return txt;
    };

所以你打电话给parse并传递你的字符串。它递归地解析您的字符串,直到找不到其他标记。这个新的工作相反,解析的项目,然后向外移动。

要实现您添加的H1示例,您可以改为使用{h1:This is the header}并将其添加到(b|i)子句中,或者在替换函数中使用switch语句而不是仅使用{{1}}语句返回1美元。

这是JSFiddle