在特定单词后断开字符串并将其保留在新行(正则表达式)

时间:2014-03-07 19:43:59

标签: javascript jquery html regex string

假设我有一个文本字段,用户可以在其中提交代码段。我想检测字符串中何时出现特定单词,然后对 之后的单词/字符执行某些操作。

假设我们有一个字符串,在睡衣之后我想在没有缩进的新行上启动其余代码。 (非常类似于代码美化器的工作方式。)输出将在pre内呈现,因此我不需要任何<br>标记或其他HTML标记。

虽然有一些捕获。

  1. 一句话后面的所有内容(睡衣)都必须在与之前一行相同的“级别”(同等数量的标签缩进)的新行上开始。
  2. 逗号应始终以新行开头,并使用标签
  3. 反向缩进
  4. 当有另一个字符时,让我们说一个惊叹号!,下面的代码必须从一个新的行开始,并以制表符作为缩进。
  5. 示例:

    输入

    Bananas! Apples and pears walk down pyjamas the street! and they say pyjamas hi to eachother, pyjamas But then! some one else comes pyjamas along pyjamas Who is he?, pyjamas I don't know who! he is pyjamas whatever,,
    

    输出

    Bananas!
        Apples and pears walk down pyjamas
        the street!
            and they say pyjamas
            hi to eachother
        , pyjamas
        But then!
            some one else comes pyjamas
            along pyjamas
            Who is he?
        , pyjamas
        I don't know who!
            he is pyjamas
            whatever
        ,
    ,
    

    我正在使用jQuery,所以如果你愿意,你可以使用它。

    这是上面代码的小提琴,所以你可以测试一下。到目前为止,My result并不是很好。 (在textarea中键入内容,输出会发生变化。)由于我目前对正则表达式知之甚少,我需要一些帮助。

    到目前为止我所拥有的:

    var a = $("textarea").val(),
        b = a.split('!').join("!\n  "),
        c = b.split('pyjamas').join("pyjamas \n");
    
    $("textarea").keyup(function() {
        $("#output>pre").html(c);
    });
    

2 个答案:

答案 0 :(得分:13)

这是一个简单的方法,不需要递归函数,甚至可以在没有正则表达式的情况下完成(但我觉得这里很方便)。

function indent(str)
{
    var tabs = function(n) { return new Array(n+1).join('\t'); }

    var tokens = str.match(/!|,|pyjamas|(?:(?!pyjamas)[^!,])+/g);
    var depth = 0;
    var result = '';
    for (var i = 0; i < tokens.length; ++i)
    {
        var token = tokens[i];
        switch(token)
        {
        case '!':
            ++depth;
            result += token + '\n' + tabs(depth);
            break;
        case ',':
            --depth;
            result += '\n' + tabs(depth) + token;
            break;
        case 'pyjamas':
            result += token + '\n' + tabs(depth);
            break;
        default:
            result += token;
            break;
        }
    }
    return result;
}

首先,我们定义一个返回n个标签字符串的函数(为方便起见)。

然后我们将流程分为两个步骤。首先我们将字符串标记 - 即我们将其分为!,pyjamas和其他任何内容。 (最后有一个关于正则表达式的解释,但你也可以通过其他方式进行标记化。)然后我们只需将标记一个接一个地保持在depth中保持当前缩进级别。

  • 如果是!,我们会增加深度,打印!,换行符和标签。
  • 如果它是,,我们会减少深度,打印换行符,选项卡,然后是,
  • 如果是pyjamas,我们只需打印它,换行符和标签。
  • 如果是其他任何内容,我们只需打印该令牌。

就是这样。你可能想要添加一些健全性检查,深度不会消极(即你有,而不是!) - 目前只是在没有任何标签的情况下渲染,但你需要写在此之后额外!以使深度恢复到1。这很容易处理,但我不知道你的假设或要求是什么。

换行后它也不会处理额外的空格(参见最后的编辑)。

Working demo.

现在是正则表达式:

/
  !               # Match a literal !
|                 # OR
  ,               # Match a literal ,
|                 # OR
  pyjamas         # Match pyjamas
|                 # OR
  (?:             # open a non-capturing group
    (?!pyjamas)   # make sure that the next character is not the 'p' of 'pyjamas'
    [^!,]         # match a non-!, non-, character
  )+              # end of group, repeat once or more (as often as possible)
/g

g找到所有匹配(而不是第一个匹配)。 ECMAScript 6将带有y modifier,这将使标记化变得更加容易 - 但令人讨厌的是,这个y修饰符是ECMAScript自己的发明,而提供此功能的所有其他风格都使用\G锚点模式。

如果你不熟悉正则表达式中的一些更高级的概念,我建议你参考这个很棒的教程:

修改

这是一个更新版本,修复了我提到的关于换行后空格的上述警告。在处理结束时,我们只需删除标签后的所有空格:

result = result.replace(/^(\t*)[ ]+/gm, '$1');

正则表达式匹配一行的开头,然后捕获零个或多个标签,然后尽可能多的空格。空间周围的方括号不是必需的,但提高了可读性。修饰符g再次找到所有此类匹配,m使^匹配行的开头(而不仅仅是字符串的开头)。在替换字符串$1中指的是我们在括号中捕获的内容 - 即所有这些标签。所以回写标签,但吞下空格。

Working demo.

答案 1 :(得分:2)

与m.buettner解决方案没有什么不同,你可以使用replace方法:

var lvl = 1;
var res = str.replace(/(!)\s*|\s*(,)|(\bpyjamas)\s+/g, function (m, g1, g2, g3) {
    if (g1) return g1 + "\n" + Array(++lvl).join("\t");
    if (g2) return "\n" + Array((lvl>1)?--lvl:lvl).join("\t") + g2;
    return g3 + "\n" + Array(lvl).join("\t"); });

console.log(res);

这个想法是使用三个不同的捕获组并在回调函数中测试它们。取决于捕获组,级别递增或递减(地面为级别1)。当级别为1并且找到逗号时,级别保持设置为1.我添加了\s*\s+以在逗号之前以及!pyjamas之后修剪空格。如果您不想这样,可以将其删除。

使用您的代码:

$("#output>pre").html($("textarea").val());

$("textarea").keyup(function() {
    $("#output>pre").html(function() {
        var lvl = 1;
        return $("textarea").val().replace(/(!)\s*|\s*(,)|(\bpyjamas)\s+/g,
            function (m, g1, g2, g3) {
                if (g1) return g1 + "\n" + Array(++lvl).join("\t");
                if (g2) return "\n" + Array((lvl>1)?--lvl:lvl).join("\t") + g2;
                return g3 + "\n" + Array(lvl).join("\t"); });
    });
});

注意:定义一个稍后可以重用的函数可能会更加干净。