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

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

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

虽然有一些捕获量。

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

例:

输入

 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,所以如果你愿意,你可以使用它。

这是上面代码的小提琴,所以你可以测试它。 到目前为止, 我的结果并不是很好。 (在textarea中输入一些东西,输出会改变。)因为我目前对正则表达式几乎不了解,所以我需要一些帮助。

到目前为止我所拥有的:

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

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

 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制表符串的n (为方便起见)。

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

  • 如果是的话! 我们增加深度,打印! ,换行符和标签。
  • 如果它是a ,我们减小深度,打印换行符,选项卡然后是,
  • 如果它是pyjamas ,我们只需打印它和换行符和标签。
  • 如果是其他任何东西,我们只需打印该令牌。

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

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

工作演示。

现在为正则表达式:

 / ! # 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修饰符 ,这将使标记化变得更加容易 - 但令人讨厌的是这个y修饰符是ECMAScript自己的发明,而提供此function的所有其他风格在模式中使用\G锚点。

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

  • 否定了角色类
  • 非捕获组
  • 向前看符号

编辑:

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

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

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

工作演示。

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

注意:定义一个稍后可以重用的函数可能更简洁。