我正在尝试将双引号替换为大括号,除非将文本包裹在某些标签中,例如[quote]和[code]。
示例输入
[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>"Why no goodbye?" replied [b]Bob[/b]. "It's always Hello!"</p>
预期产量
[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>“Why no goodbye?” replied [b]Bob[/b]. “It's always Hello!”</p>
我想出了如何通过使用(*SKIP)(*F)
在PHP中实现我想要的功能,但是我的代码将在javascript中运行,而javascript解决方案并不理想。
现在,我正在这些标签处分割字符串,运行替换,然后将字符串放在一起:
var o = 3;
a = a
.split(/(\[(?<first>(?:icode|quote|code))[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(?:\k<first>)\])/i)
.map(function(x,i) {
if (i == o-1 && x) {
x = '';
}
else if (i == o && x)
{
x = x.replace(/(?![^<]*>|[^\[]*\])"([^"]*?)"/gi, '“$1”')
o = o+3;
}
return x;
}).join('');
JavaScript正则表达式明细
split()
内部:
(\[(?<first>icode|quote|code)[^\]]*?\](?:.)*?\[\/(\k<first>)\])
-捕获括号内的模式:
\[(?<first>quote|code|icode)[^\]]*?\]
-一个[quote]
,[code]
或[icode]
的开始标签,带有或不带有=html
之类的参数,例如[code=html]
(?:[\s]*?.)*?
-任意字符(.
)的任何0+(尽可能少)出现,以空格开头或不以空格开头,因此如果开头标记后接a,则不会中断换行符[\s]*?
-超过0个空格\[\/(\k<first>)\]
-[\quote]
,[\code]
或[\icode]
结束标记。匹配(?<first>)
组中捕获的文本。例如:如果是 quote 开头标签,则将是 quote 结束标签replace()
内部:
(?![^<]*>|[^\[]*\])"([^"]*?)"
-捕获双引号内的文本:
(?![^<]*>|[^\[]*\])
-前瞻性否定,查找字符(不是<
或[
)后跟>
或]
并丢弃它们,因此它不会与内部 bbcode和html标签匹配。例如:[spoiler="Name"]
或<span style="color: #24c4f9">
。请注意,标记中的包裹匹配项保持不变。"
-文字双引号字符。([^"]*?)
-任意0+字符,双引号除外。"
-文字双引号字符。SPLIT()REGEX DEMO: https://regex101.com/r/Ugy3GG/1
那太可怕了,因为替换执行了多次。
同时,一个PHP正则表达式可以实现相同的结果。我写的正则表达式基于Match regex pattern that isn't within a bbcode tag。
(\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F)|(?![^<]*>|[^\[]*\])"([^"]*?)"
PHP正则表达式故障
(\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F)
-像上面的JavaScript split()
一样匹配捕获括号内的模式,然后(*SKIP)(*F)
使正则表达式引擎忽略匹配的文本。|
-或(?![^<]*>|[^\[]*\])"([^"]*?)"
-以与JavaScript replace()
相同的方式捕获双引号内的文本PHP DEMO: https://regex101.com/r/fB0lyI/1
此正则表达式的优点在于它只需要运行一次。请勿拆分和连接字符串。有没有办法在javascript中实现它?
答案 0 :(得分:2)
由于JS缺少回溯动词,因此您需要使用这些放在方括号中的块,但以后请按原样替换它们。通过从您自己的正则表达式获取替代的第二面,最终的正则表达式将为:
\[(quote|i?code)[^\]]*\][\s\S]*?\[\/\1\]|(?![^<]*>|[^\[]*\])"([^"]*)"
但是棘手的部分是通过replace()
方法使用回调函数:
str.replace(regex, function($0, $1, $2) {
return $1 ? $0 : '“' + $2 + '”';
})
如果第一个捕获组存在,则上述三元运算符将返回$0
(完全匹配),否则将第二个捕获组值括在引号中并返回。
注意:这在不同情况下可能会失败。
答案 1 :(得分:1)
嵌套标记很难用rx解析,尤其是JS的RegExp。复杂的正则表达式也很难读取,维护和调试。如果您的需求很简单,可以用标签内容替换掉一些禁止使用的标签,那么可以考虑使用一种简单的基于代码的替代RegExps的方法:
function curly(str) {
var excludes = {
quote: 1,
code: 1,
icode: 1
},
xpath = [];
return str.split(/(\[[^\]]+\])/) // breakup by tag markup
.map(x => { // for each tag and content:
if (x[0] === "[") { // tag markup:
if (x[1] === "/") { // close tag
xpath.pop(); // remove from current path
} else { // open tag
xpath.push(x.slice(1).split(/\W/)[0]); // add to current path
} //end if open/close tag
} else { // tag content
if (xpath.every(tag =>!excludes[tag])) x = x.replace(/"/g, function repr() {
return (repr.z = !repr.z) ? "“" : "”"; // flip flop return value (naive)
});
} //end if markup or content?
return x;
}) // end term map
.join("");
} /* end curly() */
var input = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>"Why no goodbye?" replied [b]Bob[/b]. "It's always Hello!"</p>`;
var wants = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>“Why no goodbye?” replied [b]Bob[/b]. “It's always Hello!”</p>`;
curly(input) == wants; // true
在我看来,即使更长一点,代码也允许文档,缩进和显式命名,使这些半复杂的逻辑操作更易于理解。
如果您的需求更加复杂,请为JavaScript使用真正的BBCode解析器,并根据需要映射/过滤/减少其模型。